revise the input system to better align with ANS FORTH

This commit is contained in:
Jesse D. McDonald 2020-10-14 02:11:35 -05:00
parent 3ae2ff353b
commit c0391de969
2 changed files with 412 additions and 171 deletions

View File

@ -6,7 +6,6 @@
.set JUMPFORTH_VERSION,1 .set JUMPFORTH_VERSION,1
.set BUFFER_SIZE,4096
.set RETURN_STACK_SIZE,8192 .set RETURN_STACK_SIZE,8192
.set DATA_SEGMENT_INITIAL_SIZE,65536 .set DATA_SEGMENT_INITIAL_SIZE,65536
@ -185,9 +184,6 @@ defconst VERSION,JUMPFORTH_VERSION
defconst R0,return_stack_top defconst R0,return_stack_top
defconst BUFFER,buffer
defconst __BUFFER_SIZE,BUFFER_SIZE,"BUFFER_SIZE"
defconst __DOCOL,DOCOL,"DOCOL" defconst __DOCOL,DOCOL,"DOCOL"
defconst __DODATA,DODATA,"DODATA" defconst __DODATA,DODATA,"DODATA"
defconst __DOLOAD,DOLOAD,"DOLOAD" defconst __DOLOAD,DOLOAD,"DOLOAD"
@ -616,13 +612,11 @@ defconst SIGABRT,6
defvalue C0 /* first byte of the heap */ defvalue C0 /* first byte of the heap */
defvalue S0 /* initial (empty) data stack pointer */ defvalue S0 /* initial (empty) data stack pointer */
/* STATE controls whether we are currently executing code (0) or compiling (1) */ /* STATE controls whether we are currently interpreting (0) or compiling (1) */
defvar STATE,0 /* default to executing code */ defvar STATE,0 /* default to interpreting */
/* Initially the KEY function "reads" the embedded file "startup.4th". */ /* >IN gives the current offset of the parse area within the input buffer */
/* When this is exhausted the pointers are reset to point to the input buffer. */ defvar IN,0,">IN"
defvar CURRKEY,startup_defs
defvar BUFFTOP,startup_defs_end
/* NOTE: These are initialized in _start but vary during runtime. */ /* NOTE: These are initialized in _start but vary during runtime. */
defvar CP /* "compilation pointer", next free byte in the heap */ defvar CP /* "compilation pointer", next free byte in the heap */
@ -1051,7 +1045,7 @@ defcode FILL
rep stosb rep stosb
NEXT NEXT
/* ( src dst n -- ) ( block copy n bytes from src to dst ) */ /* ( src dst n -- ) Block copy n bytes from src to dst (ascending addresses) */
defcode CMOVE defcode CMOVE
mov %esi,%edx mov %esi,%edx
pop %ecx pop %ecx
@ -1061,6 +1055,20 @@ defcode CMOVE
mov %edx,%esi mov %edx,%esi
NEXT NEXT
/* ( src dst n -- ) Block copy n bytes from src to dst (descending addresses) */
defcode CMOVE_UP,"CMOVE>"
mov %esi,%edx
pop %ecx
pop %edi
pop %esi
lea -1(%edi,%ecx),%edi
lea -1(%esi,%ecx),%esi
std
rep movsb
cld
mov %edx,%esi
NEXT
/* ( a -- ) ( R: -- a ) */ /* ( a -- ) ( R: -- a ) */
defcode TOR,">R" defcode TOR,">R"
pop %eax pop %eax
@ -1101,7 +1109,7 @@ defcode TWOFROMR,"2R>"
push %ebx push %ebx
NEXT NEXT
/* (R: a b -- a b ) ( -- a b ) */ /* ( R: a b -- a b ) ( -- a b ) */
defcode TWORFETCH,"2R@" defcode TWORFETCH,"2R@"
movl (%ebp),%ebx movl (%ebp),%ebx
movl 4(%ebp),%eax movl 4(%ebp),%eax
@ -1109,27 +1117,77 @@ defcode TWORFETCH,"2R@"
push %ebx push %ebx
NEXT NEXT
/* ( xu ... x1 u -- ) ( R: -- xu ... x1 u ) */
defcode NTOR,"N>R"
mov %esi,%edx
movl (%esp),%ecx
add $1,%ecx
mov %ecx,%ebx
shl $2,%ebx
sub %ebx,%ebp
mov %esp,%esi
mov %ebp,%edi
rep movsd
mov %esi,%esp
mov %edx,%esi
NEXT
/* ( R: xu ... x1 u -- ) ( -- xu ... x1 u ) */
defcode NFROMR,"NR>"
mov %esi,%edx
movl (%ebp),%ecx
add $1,%ecx
mov %ecx,%ebx
shl $2,%ebx
sub %ebx,%esp
mov %ebp,%esi
mov %esp,%edi
rep movsd
mov %esi,%ebp
mov %edx,%esi
NEXT
/* ( R: xu ... x1 u -- xu ... x1 u ) ( -- xu ... x1 u ) */
defcode NRFETCH,"NR@"
mov %esi,%edx
movl (%ebp),%ecx
add $1,%ecx
mov %ecx,%ebx
shl $2,%ebx
sub %ebx,%esp
mov %ebp,%esi
mov %esp,%edi
rep movsd
mov %edx,%esi
NEXT
/* ( -- a-addr ) */
defcode RSPFETCH,"RSP@" defcode RSPFETCH,"RSP@"
push %ebp push %ebp
NEXT NEXT
/* ( a-addr -- ) */
defcode RSPSTORE,"RSP!" defcode RSPSTORE,"RSP!"
pop %ebp pop %ebp
NEXT NEXT
/* ( R: x -- ) */
defcode RDROP defcode RDROP
addl $4,%ebp addl $4,%ebp
NEXT NEXT
/* ( R: a b -- ) */
defcode TWORDROP,"2RDROP" defcode TWORDROP,"2RDROP"
addl $8,%ebp addl $8,%ebp
NEXT NEXT
/* ( -- a-addr ) Get the data stack pointer (address of cell below a-addr) */
defcode SPFETCH,"SP@" defcode SPFETCH,"SP@"
mov %esp,%eax mov %esp,%eax
push %eax push %eax
NEXT NEXT
/* ( a-addr -- ) Set the data stack pointer */
defcode SPSTORE,"SP!" defcode SPSTORE,"SP!"
pop %esp pop %esp
NEXT NEXT
@ -1257,12 +1315,17 @@ defcode BREAK
.section .data .section .data
bootstrap_data_begin: bootstrap_data_begin:
/* ( c-addr u -- "ccc" ) */
defword TYPE defword TYPE
.int LIT,1,NROT,SYS_WRITE,SYSCALL3,DROP,EXIT .int LIT,1,NROT,SYS_WRITE,SYSCALL3,DROP,EXIT
/* ( c -- "c" ) */
defword EMIT
.int SPFETCH,LIT,1,SWAP,LIT,1,SYS_WRITE,SYSCALL3,TWODROP,EXIT
/* ( -- "<eol>" ) */
defword EOL defword EOL
litstring "\n" .int LIT,10,EMIT,EXIT
.int TYPE,EXIT
/* Used for any fatal errors that occur during bootstrapping */ /* Used for any fatal errors that occur during bootstrapping */
defword BAILOUT defword BAILOUT
@ -1271,33 +1334,43 @@ defword BAILOUT
.int TYPE .int TYPE
0: .int LIT,254,SYS_EXIT,SYSCALL1,DROP,BRANCH,(0b - .) 0: .int LIT,254,SYS_EXIT,SYSCALL1,DROP,BRANCH,(0b - .)
/* Simplified KEY that can only read from the pre-filled input buffer */ defword UNEXPECTED_EOF,"UNEXPECTED-EOF"
/* The startup.4th should replace this with a more complete version */ litstring "Unexpected end of input\n"
/* If the input ends while this version is still in use the program will terminate */
defword KEY
.int CURRKEY,FETCH,DUP,BUFFTOP,FETCH,GE,ZBRANCH,(1f - .)
litstring "Unexpected end of buffer\n"
.int TYPE,BAILOUT .int TYPE,BAILOUT
1: .int DUP,ADD1,CURRKEY,STORE,FETCHBYTE,EXIT
/* Puts the most recently read key back in the input buffer */ /* During bootstrapping the source buffer is the embedded file "startup.4th". */
/* CAUTION: Can only safely be used ONCE after each call to KEY! */ /* ( -- c-addr u ) */
defword PUTBACK defword SOURCE
.int CURRKEY,FETCH,SUB1,CURRKEY,STORE,EXIT .int LIT,startup_defs,LIT,(startup_defs_end - startup_defs),EXIT
/* ( c-addr u1 u2 -- c-addr+u2 u1-u2 ) */
defword DROP_PREFIX,"DROP-PREFIX"
.int ROT,OVER,ADD,NROT,SUB,EXIT
/* ( -- c-addr u ) Current parse area (input buffer minus first >IN characters) */
defword PARSE_AREA,"PARSE-AREA"
.int SOURCE,IN,FETCH,DROP_PREFIX,EXIT
/* ( "c" -- c ) Leaves c at the start of the parse area */
defword PEEK_CHAR,"PEEK-CHAR"
.int PARSE_AREA,ZBRANCH,(0f - .),FETCHBYTE,EXIT
0: .int UNEXPECTED_EOF
/* ( "c" -- c ) Removes and returns the first character in the parse area */
defword NEXT_CHAR,"NEXT-CHAR"
.int PEEK_CHAR,LIT,1,IN,INCREMENT,EXIT
/* ( c -- flag ) */
defword ISSPACE,"SPACE?" defword ISSPACE,"SPACE?"
/* check for space (32) first and return true if input matches */ /* check for space (32) first and return true if input matches */
.int DUP,LIT,32,EQU,QDUP,ZBRANCH,(0f - .),NIP,EXIT .int DUP,LIT,32,EQU,QDUP,ZBRANCH,(0f - .),NIP,EXIT
/* otherwise test for 9...13 inclusive (HT, LF, VT, FF, CR) */ /* otherwise test for 9...13 inclusive (HT, LF, VT, FF, CR) */
0: .int LIT,9,SUB,LIT,(13 - 9),ULT,EXIT 0: .int LIT,9,SUB,LIT,(13 - 9),ULT,EXIT
defword SKIPSPACE
0: .int KEY,ISSPACE,ZEQU,ZBRANCH,(0b - .),PUTBACK,EXIT
/* Simplified version that can only work within the preallocated data region */ /* Simplified version that can only work within the preallocated data region */
/* The startup.4th should replace this with a more complete version */ /* The startup.4th should replace this with a more complete version */
defword ALLOT defword ALLOT
.int CP,FETCH,ADD,CP,STORE,EXIT .int CP,INCREMENT,EXIT
defword COMMA,"," defword COMMA,","
.int CP,FETCH,CELL,ALLOT,STORE,EXIT .int CP,FETCH,CELL,ALLOT,STORE,EXIT
@ -1399,19 +1472,47 @@ defword FIND_OR_ABORT,"FIND-OR-ABORT"
0: litstring "Word not found: " 0: litstring "Word not found: "
.int TYPE,TYPE,EOL,BAILOUT .int TYPE,TYPE,EOL,BAILOUT
/* ( "<spaces>" -- ) */
defword SKIPSPACE
0: .int PARSE_AREA,ZBRANCH,(1f - .)
.int FETCHBYTE,ISSPACE,ZBRANCH,(2f - .)
.int LIT,1,IN,INCREMENT,BRANCH,(0b - .)
1: .int DROP
2: .int EXIT
/* ( "<spaces?>ccc" -- c-addr u ) */
defword WORD defword WORD
.int SKIPSPACE,CP,FETCH .int SKIPSPACE
0: .int KEY,DUP,ISSPACE,ZBRANCH,(1f - .) .int PARSE_AREA,DROP,LIT,1
.int DROP,PUTBACK,CP,FETCH,OVER,SUB,OVER,CP,STORE,EXIT .int NEXT_CHAR,DROP
1: .int COMMABYTE,BRANCH,(0b - .) 0: .int PARSE_AREA,ZBRANCH,(1f - .)
.int FETCHBYTE,ISSPACE,ZEQU,ZBRANCH,(2f - .)
.int ADD1,LIT,1,IN,INCREMENT,BRANCH,(0b - .)
1: .int DROP
2: .int EXIT
defword ESCAPED_CHAR
.int NEXT_CHAR,DUP,LIT,'\\',NEQU,ZBRANCH,(0f - .),EXIT
0: .int DROP,NEXT_CHAR
.int LIT,'0',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,0,EXIT
0: .int LIT,'a',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,7,EXIT
0: .int LIT,'b',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,8,EXIT
0: .int LIT,'t',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,9,EXIT
0: .int LIT,'n',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,10,EXIT
0: .int LIT,'v',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,11,EXIT
0: .int LIT,'f',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,12,EXIT
0: .int LIT,'r',OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,13,EXIT
0: .int LIT,34,OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,34,EXIT /* double-quote */
0: .int LIT,39,OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,39,EXIT /* single-quote */
0: .int LIT,92,OVER,EQU,ZBRANCH,(0f - .),DROP,LIT,92,EXIT /* backslash */
0: litstring "Unknown escape sequence: \\"
.int TYPE,EMIT,EOL,BAILOUT
defword READSTRING defword READSTRING
.int CP,FETCH .int CP,FETCH
0: .int KEY,DUP,LIT,'\\',EQU,ZBRANCH,(1f - .) 0: .int PEEK_CHAR,LIT,34,NEQU,ZBRANCH,(1f - .)
.int DROP,KEY,BRANCH,(2f - .) .int ESCAPED_CHAR,COMMABYTE,BRANCH,(0b - .)
1: .int DUP,LIT,'"',EQU,ZBRANCH,(2f - .) 1: .int LIT,1,IN,INCREMENT,CP,FETCH,OVER,SUB,ALIGN,EXIT
.int DROP,CP,FETCH,OVER,SUB,ALIGN,EXIT
2: .int COMMABYTE,BRANCH,(0b - .)
defword PARSENUMBER defword PARSENUMBER
.int DUP,LIT,0,GT,ZBRANCH,(6f - .) .int DUP,LIT,0,GT,ZBRANCH,(6f - .)
@ -1431,15 +1532,15 @@ defword PARSENUMBER
defword INTERPRET defword INTERPRET
.int SKIPSPACE .int SKIPSPACE
.int KEY,LIT,'"',EQU,ZBRANCH,(1f - .) .int PEEK_CHAR,LIT,34,EQU,ZBRANCH,(1f - .)
.int LIT,1,IN,INCREMENT
.int STATE,FETCH,ZBRANCH,(0f - .) .int STATE,FETCH,ZBRANCH,(0f - .)
.int LIT,LITSTRING,COMMA,CP,FETCH,LIT,0,COMMABYTE .int LIT,LITSTRING,COMMA,CP,FETCH,LIT,0,COMMABYTE
.int READSTRING,ROT,STOREBYTE,DROP,ALIGN,EXIT .int READSTRING,ROT,STOREBYTE,DROP,ALIGN,EXIT
/* ELSE */ /* ELSE */
0: .int READSTRING,EXIT 0: .int READSTRING,EXIT
/* ELSE */ /* ELSE */
1: .int PUTBACK,LIT,64,ALLOT,WORD,LIT,-64,ALLOT 1: .int WORD,TWODUP,PARSENUMBER,ZBRANCH,(3f - .)
.int TWODUP,PARSENUMBER,ZBRANCH,(3f - .)
.int NROT,TWODROP .int NROT,TWODROP
.int STATE,FETCH,ZBRANCH,(2f - .) .int STATE,FETCH,ZBRANCH,(2f - .)
.int LIT,LIT,COMMA,COMMA .int LIT,LIT,COMMA,COMMA
@ -1452,7 +1553,7 @@ defword INTERPRET
4: .int EXECUTE,EXIT 4: .int EXECUTE,EXIT
/* ELSE */ /* ELSE */
5: .int DUP,ISBOOTSTRAP,ZBRANCH,(6f - .) 5: .int DUP,ISBOOTSTRAP,ZBRANCH,(6f - .)
litstring "Compiled bootstrap word: " litstring "Tried to compile bootstrap word: "
.int TYPE,TNAME,TYPE,EOL,BAILOUT .int TYPE,TNAME,TYPE,EOL,BAILOUT
6: .int COMMA,EXIT 6: .int COMMA,EXIT
@ -1466,10 +1567,11 @@ defword CREATE
.int LIT,DODATA,COMMA .int LIT,DODATA,COMMA
.int LIT,0,COMMA .int LIT,0,COMMA
.int CURRENT,FETCH,FETCH,COMMA .int CURRENT,FETCH,FETCH,COMMA
.int LIT,0,COMMABYTE .int WORD
.int WORD,NIP,DUP,ALLOT,ALIGN .int DUP,COMMABYTE
.int OVER,TFLAGS,STOREBYTE .int CP,FETCH,SWAP
.int CP,FETCH,OVER,TDFA,STORE .int DUP,ALLOT,CMOVE
.int ALIGN,CP,FETCH,OVER,TDFA,STORE
.int CURRENT,FETCH,STORE,EXIT .int CURRENT,FETCH,STORE,EXIT
/* /*
@ -1484,10 +1586,10 @@ defword RBRACKET,"]",F_IMMED
.int TRUE,STATE,STORE,EXIT .int TRUE,STATE,STORE,EXIT
defword SLASH,"\\",F_IMMED defword SLASH,"\\",F_IMMED
0: .int KEY,LIT,10,EQU,ZBRANCH,(0b - .),EXIT 0: .int NEXT_CHAR,LIT,10,EQU,ZBRANCH,(0b - .),EXIT
defword PAREN,"(",F_IMMED defword PAREN,"(",F_IMMED
0: .int KEY,LIT,')',EQU,ZBRANCH,(0b - .),EXIT 0: .int NEXT_CHAR,LIT,')',EQU,ZBRANCH,(0b - .),EXIT
defword COLON,":" defword COLON,":"
/* Make word & fetch address */ /* Make word & fetch address */
@ -1521,6 +1623,12 @@ defword LITERAL,,F_IMMED
defword COMPILE_QUOTE,"[']",F_IMMED defword COMPILE_QUOTE,"[']",F_IMMED
.int QUOTE,LITERAL,EXIT .int QUOTE,LITERAL,EXIT
defword CHAR
.int WORD,DROP,FETCHBYTE,EXIT
defword COMPILE_CHAR,"[CHAR]",F_IMMED
.int CHAR,LITERAL,EXIT
defword POSTPONE,,F_IMMED defword POSTPONE,,F_IMMED
.int WORD,FIND_OR_ABORT,ZGT,ZBRANCH,(0f - .) .int WORD,FIND_OR_ABORT,ZGT,ZBRANCH,(0f - .)
.int LITERAL .int LITERAL
@ -1589,8 +1697,3 @@ startup_defs_end:
return_stack: return_stack:
.space RETURN_STACK_SIZE .space RETURN_STACK_SIZE
return_stack_top: return_stack_top:
.bss
.align 4096
buffer:
.space BUFFER_SIZE

View File

@ -116,6 +116,40 @@
SYS_GETPID SYSCALL0 SYS_GETTID SYSCALL0 SIGABRT SYS_TGKILL SYSCALL3 DROP SYS_GETPID SYSCALL0 SYS_GETTID SYSCALL0 SIGABRT SYS_TGKILL SYSCALL3 DROP
AGAIN ; AGAIN ;
\ Display the unsigned number at the top of the stack
: U. ( u -- "<digits>" )
\ Start with the highest place-value on the left
1000000000
\ Skip place-values that would be larger than the input
BEGIN 2DUP U< OVER 1 U> AND WHILE 10 U/ REPEAT
\ Emit the remaining digits down to the units' place
BEGIN
TUCK U/MOD [CHAR] 0 + EMIT SWAP
DUP 1 U<= IF 2DROP EXIT THEN
10 U/
AGAIN ;
\ Display the signed number at the top of the stack
: . ( n -- "<minus?><digits>" )
DUP 0< IF [CHAR] - EMIT NEGATE THEN U. ;
\ Display the content of the data stack
: .DS ( -- "<text>" )
SP@ S0
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ .
BEGIN
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ SPACE .
AGAIN ;
\ Display the content of the return stack
: .RS ( -- "<text>" )
\ Skip the topmost cell, which is the return address for the call to .RS
RSP@ CELL + R0
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ .
BEGIN
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ SPACE .
AGAIN ;
\ Return the next address in the compilation/data area \ Return the next address in the compilation/data area
: HERE ( -- addr ) CP @ ; : HERE ( -- addr ) CP @ ;
@ -184,6 +218,15 @@
: ELSE ( C: orig1 -- orig2 ) IMMEDIATE : ELSE ( C: orig1 -- orig2 ) IMMEDIATE
POSTPONE AHEAD SWAP POSTPONE THEN ; POSTPONE AHEAD SWAP POSTPONE THEN ;
\ Short-circuit logical operators
\ Examples:
\ <cond1> AND-THEN <cond2> THEN
\ <cond1> OR-ELSE <cond2> THEN
: AND-THEN ( C: -- orig ) ( Runtime S: flag -- FALSE | <dropped> ) IMMEDIATE
POSTPONE DUP POSTPONE IF POSTPONE DROP ;
: OR-ELSE ( C: -- orig ) ( Runtime S: flag -- nonzero-flag | <dropped> ) IMMEDIATE
POSTPONE ?DUP POSTPONE 0= POSTPONE IF ;
\ Unbounded loop: BEGIN <body> AGAIN \ Unbounded loop: BEGIN <body> AGAIN
\ BEGIN places the offset of the start of <code> on the stack. \ BEGIN places the offset of the start of <code> on the stack.
\ AGAIN creates a relative branch back to the start of <code>. \ AGAIN creates a relative branch back to the start of <code>.
@ -247,52 +290,116 @@
: ENDCASE ( C: orign ... orig1 n -- ) IMMEDIATE : ENDCASE ( C: orign ... orig1 n -- ) IMMEDIATE
POSTPONE DROP 0 DO POSTPONE THEN LOOP ; POSTPONE DROP 0 DO POSTPONE THEN LOOP ;
\ If the input buffer is empty, refill it from stdin SOURCE
\ Return the next character from the input buffer CREATE INPUT-BUFFER-SIZE ,
: KEY CREATE INPUT-BUFFER ,
CURRKEY @
DUP BUFFTOP @ >= IF CREATE CURRENT-SOURCE-ID -1 ,
DROP BUFFER 0 OVER BUFFER_SIZE SYS_READ SYSCALL3
DUP 0<= IF : SOURCE ( -- c-addr u ) INPUT-BUFFER @ INPUT-BUFFER-SIZE @ ;
2DROP : SOURCE-ID ( -- 0 | -1 | +n ) CURRENT-SOURCE-ID @ ;
BYE
: SAVE-INPUT ( -- xu ... x1 u ) >IN @ 1 ;
: RESTORE-INPUT ( xu ... x1 u -- ) OVER >IN ! NDROP ;
\ The size of this buffer will determine the maximum line length
4096 CONSTANT TERMINAL-BUFFER-SIZE
CREATE TERMINAL-BUFFER TERMINAL-BUFFER-SIZE ALLOT
CREATE TIB-LEFTOVER 0 ,
CREATE TIB-LEFTOVER-SIZE 0 ,
: REFILL ( -- flag )
SOURCE-ID 0< IF FALSE EXIT THEN
\ Shift any leftover characters after the previous line to the start of the buffer
TIB-LEFTOVER @ TERMINAL-BUFFER TIB-LEFTOVER-SIZE @ CMOVE
\ Look for the linefeed character which marks the end of the first line
TIB-LEFTOVER-SIZE @ 0 BEGIN
\ If at the end with room in the buffer, read more from the file descriptor
2DUP = IF
DUP TERMINAL-BUFFER-SIZE U< IF
\ SOURCE-ID is the file descriptor number to read from
SOURCE-ID OVER DUP TERMINAL-BUFFER + SWAP TERMINAL-BUFFER-SIZE SWAP -
( S: length idx src-id buff buff-size )
\ Repeat read if interrupted by a signal (returns -EINTR)
BEGIN
SYS_READ SYSCALL3
DUP ERRNO_EINTR NEGATE <>
UNTIL
\ Any other negative (error) return value is fatal
DUP 0< IF
DROP "Error occurred while reading input\n" TYPE
FATAL-ERROR
THEN
( S: length idx u-read )
\ Add the amount of data read to the length; index is unchanged
ROT + SWAP
THEN
THEN THEN
OVER + BUFFTOP ! \ At this point if index equals length then buffer is full or read returned 0
THEN \ Either way, we won't be reading any more into the buffer
DUP 1+ CURRKEY ! C@ 2DUP = OR-ELSE
; \ Check if the next character is a linefeed
1+ DUP 1- TERMINAL-BUFFER + C@ LF =
THEN
UNTIL
( S: length idx )
\ idx is the next location after the linefeed, if found, or else equal to length
\ Save the rest, if any, for the next REFILL
DUP TERMINAL-BUFFER + TIB-LEFTOVER !
TUCK - TIB-LEFTOVER-SIZE !
( S: idx )
\ The new input buffer is the first idx characters of the terminal buffer
TERMINAL-BUFFER INPUT-BUFFER !
DUP INPUT-BUFFER-SIZE !
DUP IF 0 >IN ! THEN
0<> ;
\ Puts the most recently read key back in the input buffer : DROP-PREFIX ( c-addr u1 u2 -- c-addr+u2 u1-u2 ) ROT OVER + -ROT - ;
\ CAUTION: Can only safely be used ONCE after each call to KEY!
\ This takes advantage of the fact that the key is still in the buffer
: PUTBACK CURRKEY @ 1- CURRKEY ! ;
: SKIPSPACE BEGIN KEY SPACE? INVERT UNTIL PUTBACK ; : PARSE-AREA ( -- c-addr u ) SOURCE >IN @ DROP-PREFIX ;
: PARSE-EMPTY? ( -- flag ) SOURCE NIP >IN @ = ;
: PEEK-CHAR ( -- c )
PARSE-AREA 0= IF
DROP "Unexpected end of input\n" TYPE
FATAL-ERROR
THEN C@ ;
: SKIP-CHAR ( -- ) 1 >IN +! ;
: NEXT-CHAR ( -- c ) PEEK-CHAR SKIP-CHAR ;
: SKIPSPACE ( "<spaces?>" -- )
BEGIN
PARSE-EMPTY? 0= AND-THEN PEEK-CHAR SPACE? THEN
WHILE
SKIP-CHAR
REPEAT ;
\ Comments; ignore all characters until the next EOL or ) character, respectively
: \ ( "ccc<eol>" -- ) IMMEDIATE BEGIN NEXT-CHAR LF = UNTIL ;
: ( ( "ccc<closeparen>" -- ) IMMEDIATE BEGIN NEXT-CHAR [CHAR] ) = UNTIL ;
\ Skip whitespace; read and return the next word delimited by whitespace \ Skip whitespace; read and return the next word delimited by whitespace
\ The word is stored in contiguous but *unallocated* data space \ The delimiting whitespace character is left in the parse area
\ The delimiting whitespace character is left in the input buffer
: WORD ( "<spaces>ccc" -- c-addr u ) : WORD ( "<spaces>ccc" -- c-addr u )
SKIPSPACE SKIPSPACE
HERE PARSE-AREA DROP
BEGIN BEGIN
KEY DUP SPACE? 0= PARSE-EMPTY? OR-ELSE PEEK-CHAR SPACE? THEN 0=
WHILE WHILE
C, SKIP-CHAR
REPEAT REPEAT
DROP PARSE-AREA DROP OVER - ;
PUTBACK
HERE OVER -
OVER CP ! ;
: CREATE ( "<spaces>ccc" -- ) : CREATE ( "<spaces>ccc" -- )
ALIGN HERE ALIGN HERE
DODATA , 0 , LATEST , DODATA , 0 , LATEST ,
HERE 0 C, WORD WORD DUP C, HERE SWAP DUP ALLOT CMOVE
NIP DUP ALLOT ALIGN SWAP C! ALIGN HERE OVER >DFA !
HERE OVER >DFA ! GET-CURRENT ! ;
GET-CURRENT !
;
\ Called when a word using DOES> is executed (not compiled) to set \ Called when a word using DOES> is executed (not compiled) to set
\ the runtime behavior of the most recently defined word \ the runtime behavior of the most recently defined word
@ -328,6 +435,8 @@
\ name execution: ( -- value ) \ name execution: ( -- value )
: VALUE CREATE , DOES> @ ; : VALUE CREATE , DOES> @ ;
: (TRACE) >NAME TYPE SPACE .DS EOL ;
\ Define a threaded FORTH word \ Define a threaded FORTH word
\ The word is initially hidden so it can refer to a prior word with the same name \ The word is initially hidden so it can refer to a prior word with the same name
\ The definition is terminated with the ; immediate word, which unhides the name \ The definition is terminated with the ; immediate word, which unhides the name
@ -335,7 +444,9 @@
CREATE LATEST CREATE LATEST
DUP >FLAGS DUP C@ F_HIDDEN OR SWAP C! DUP >FLAGS DUP C@ F_HIDDEN OR SWAP C!
DOCOL SWAP >CFA ! DOCOL SWAP >CFA !
POSTPONE ] ; POSTPONE ]
\ ( uncomment for tracing ) LATEST POSTPONE LITERAL POSTPONE (TRACE)
;
\ Like : but the definition has no name \ Like : but the definition has no name
\ The zero-length name still included in the word list so LATEST can refer to it \ The zero-length name still included in the word list so LATEST can refer to it
@ -452,23 +563,6 @@
THEN THEN
AGAIN ; AGAIN ;
\ Display the unsigned number at the top of the stack
: U. ( u -- "<digits>" )
\ Start with the highest place-value on the left
1000000000
\ Skip place-values that would be larger than the input
BEGIN 2DUP U< OVER 1 U> AND WHILE 10 U/ REPEAT
\ Emit the remaining digits down to the units' place
BEGIN
TUCK U/MOD [CHAR] 0 + EMIT SWAP
DUP 1 U<= IF 2DROP EXIT THEN
10 U/
AGAIN ;
\ Display the signed number at the top of the stack
: . ( n -- "<minus?><digits>" )
DUP 0< IF [CHAR] - EMIT NEGATE THEN U. ;
\ Field accessors for the search order linked list \ Field accessors for the search order linked list
: ORDER>LINK ( a-addr1 -- a-addr2 ) ; : ORDER>LINK ( a-addr1 -- a-addr2 ) ;
: ORDER>WID ( a-addr1 -- a-addr2 ) CELL+ ; : ORDER>WID ( a-addr1 -- a-addr2 ) CELL+ ;
@ -605,8 +699,7 @@ DEFER ABORT ( -- <noreturn> )
\ Same as FIND except that unknown words are reported and result in a call to ABORT \ Same as FIND except that unknown words are reported and result in a call to ABORT
: FIND-OR-ABORT ( c-addr u -- xt 1 | xt -1 ) : FIND-OR-ABORT ( c-addr u -- xt 1 | xt -1 )
FIND ?DUP IF EXIT THEN FIND ?DUP 0= IF "UNKNOWN WORD: " TYPE TYPE EOL ABORT THEN ;
"UNKNOWN WORD: " TYPE TYPE EOL ABORT ;
\ Read a word from the input (during runtime) and return its execution token \ Read a word from the input (during runtime) and return its execution token
\ Aborts if the word is not found in the current (runtime) search order list \ Aborts if the word is not found in the current (runtime) search order list
@ -658,6 +751,25 @@ HIDE ORDER-FREELIST
HIDE ORDER>WID HIDE ORDER>WID
HIDE ORDER>LINK HIDE ORDER>LINK
: ESCAPED-CHAR ( "<escapeseq>" | "c" -- c )
NEXT-CHAR DUP [CHAR] \ = IF
DROP NEXT-CHAR CASE
[CHAR] 0 OF 0 ENDOF
[CHAR] a OF 7 ENDOF
[CHAR] b OF 8 ENDOF
[CHAR] t OF 9 ENDOF
[CHAR] n OF 10 ENDOF
[CHAR] v OF 11 ENDOF
[CHAR] f OF 12 ENDOF
[CHAR] r OF 13 ENDOF
[CHAR] " OF [CHAR] " ENDOF
[CHAR] ' OF [CHAR] ' ENDOF
[CHAR] \ OF [CHAR] \ ENDOF
"Unknown escape sequence: \\" TYPE DUP EMIT EOL
FATAL-ERROR
ENDCASE
THEN ;
\ Read a literal character string up to the next double-quote character \ Read a literal character string up to the next double-quote character
\ Unlike WORD the string is stored in contiguous *allocated* data space \ Unlike WORD the string is stored in contiguous *allocated* data space
\ The delimiting double-quote character is removed from the input buffer \ The delimiting double-quote character is removed from the input buffer
@ -665,17 +777,12 @@ HIDE ORDER>LINK
: READSTRING ( "ccc<doublequote>" -- c-addr u ) : READSTRING ( "ccc<doublequote>" -- c-addr u )
HERE HERE
BEGIN BEGIN
KEY PEEK-CHAR [CHAR] " <>
DUP [CHAR] \ = IF
DROP KEY TRUE
ELSE
DUP [CHAR] " <>
THEN
WHILE WHILE
C, ESCAPED-CHAR C,
REPEAT REPEAT
DROP HERE OVER - SKIP-CHAR
; HERE OVER - ;
: PARSENUMBER ( c-addr u -- n TRUE | c-addr u FALSE ) : PARSENUMBER ( c-addr u -- n TRUE | c-addr u FALSE )
DUP 0= IF FALSE EXIT THEN DUP 0= IF FALSE EXIT THEN
@ -700,38 +807,68 @@ HIDE ORDER>LINK
\ Read a word, number, or string and either execute it or compile it \ Read a word, number, or string and either execute it or compile it
\ The stack effect depends on the input and the current value of STATE \ The stack effect depends on the input and the current value of STATE
: INTERPRET ( i*x "<spaces>ccc" -- j*x ) : INTERPRET ( i*x "<spaces>ccc" -- j*x )
SKIPSPACE BEGIN
KEY SKIPSPACE
[CHAR] " = IF PARSE-EMPTY? 0=
STATE @ IF WHILE
POSTPONE LITSTRING PEEK-CHAR [CHAR] " = IF
HERE 0 C, SKIP-CHAR
READSTRING NIP SWAP C! ALIGN
ELSE
READSTRING
THEN
ELSE
PUTBACK 64 ALLOT WORD -64 ALLOT
PARSENUMBER IF
STATE @ IF STATE @ IF
POSTPONE LITERAL POSTPONE LITSTRING
HERE 0 C,
READSTRING NIP SWAP C! ALIGN
ELSE
READSTRING
THEN THEN
ELSE ELSE
FIND-OR-ABORT WORD
\ -1 => immediate word; execute regardless of STATE PARSENUMBER IF
\ 1 => read STATE; compile if true, execute if false STATE @ IF
0< IF EXECUTE EXIT THEN POSTPONE LITERAL
STATE @ IF COMPILE, EXIT THEN THEN
EXECUTE ELSE
FIND-OR-ABORT
\ -1 => immediate word; execute regardless of STATE
\ 1 => read STATE; compile if true, execute if false
0< OR-ELSE STATE @ 0= THEN IF EXECUTE ELSE COMPILE, THEN
THEN
THEN THEN
THEN ; REPEAT ;
\ Comments; ignore all characters until the next EOL or ) character, respectively \ Empty the return stack, make stdin the input source, and enter interpretation state
: \ ( "ccc<eol>" -- ) IMMEDIATE BEGIN KEY LF = UNTIL ; : QUIT ( -- <noreturn> )
: ( ( "ccc<closeparen>" -- ) IMMEDIATE BEGIN KEY [CHAR] ) = UNTIL ; R0 RSP!
0 CURRENT-SOURCE-ID !
FALSE STATE !
BEGIN
REFILL 0= IF
EOL BYE
THEN
INTERPRET
STATE @ 0= IF
"OK> " TYPE
THEN
AGAIN ;
\ Empty the return stack and enter interpretation state : EVALUATE ( i*x c-addr u -- j*x )
: QUIT ( -- <noreturn> ) R0 RSP! FALSE STATE ! BEGIN INTERPRET AGAIN ; SAVE-INPUT N>R
SOURCE 2>R
SOURCE-ID >R
INPUT-BUFFER-SIZE !
INPUT-BUFFER !
0 >IN !
-1 CURRENT-SOURCE-ID !
INTERPRET
R> CURRENT-SOURCE-ID !
2R> INPUT-BUFFER-SIZE ! INPUT-BUFFER !
NR> RESTORE-INPUT ;
HIDE CURRENT-SOURCE-ID
HIDE INPUT-BUFFER
HIDE INPUT-BUFFER-SIZE
HIDE TERMINAL-BUFFER
HIDE TIB-LEFTOVER
HIDE TIB-LEFTOVER-SIZE
\ Redefine ABORT as a non-deferred word; update deferred references to point here \ Redefine ABORT as a non-deferred word; update deferred references to point here
\ Empty the data stack and then perform the function of QUIT without any message \ Empty the data stack and then perform the function of QUIT without any message
@ -744,7 +881,7 @@ HIDE ABORT
\ Switch to the interpreter defined in this startup file \ Switch to the interpreter defined in this startup file
FORTH-WORDLIST 1 SET-ORDER FORTH-WORDLIST 1 SET-ORDER
DEFINITIONS DEFINITIONS
QUIT { R0 RSP! BEGIN INTERPRET AGAIN } EXECUTE
\ ***************************************************************************** \ *****************************************************************************
\ Bootstrapping is complete \ Bootstrapping is complete
@ -754,48 +891,48 @@ QUIT
\ Return the number of words on the data stack \ Return the number of words on the data stack
: DEPTH ( -- n ) SP@ S0 SWAP - CELL / ; : DEPTH ( -- n ) SP@ S0 SWAP - CELL / ;
\ Display the content of the data stack \ Return TRUE if the given address is the execution token of a word in
: .DS ( -- "<text>" ) \ the current search order or compilation word list, or FALSE otherwise
SP@ S0 \ The word's name may be hidden or shadowed by another definition
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ . : WORD? ( addr -- flag )
BEGIN
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ SPACE .
AGAIN ;
\ Display the content of the return stack
: .RS ( -- "<text>" )
\ Skip the topmost cell, which is the return address for the call to .RS
RSP@ CELL + R0
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ .
BEGIN
CELL - 2DUP > IF 2DROP EXIT THEN DUP @ SPACE .
AGAIN ;
\ Attempt to locate a visible word whose execution token matches the given address
\ If found return the word name and TRUE; otherwise just return FALSE
: LOOKUP ( addr -- c-addr u TRUE | FALSE )
>R >R
GET-ORDER GET-ORDER
1+ GET-CURRENT SWAP
BEGIN BEGIN
?DUP ?DUP
WHILE WHILE
1- SWAP R@ FALSE ROT 1- SWAP R@ FALSE ROT
( S: widn ... wid1 n addr FALSE wid ) ( R: addr ) ( S: widn ... wid1 n addr FALSE wid ) ( R: addr )
{ ( addr FALSE xt -- addr FALSE FALSE | c-addr u TRUE TRUE ) \ Inner function: ( addr FALSE xt -- addr FALSE FALSE | addr TRUE TRUE )
NIP OVER = IF { NIP OVER = DUP } WITH-WORDLIST
>NAME TRUE NIP IF RDROP NDROP TRUE EXIT THEN
ELSE
FALSE
THEN DUP } WITH-VISIBLE
?DUP IF RDROP EXIT THEN
DROP
REPEAT REPEAT
RDROP FALSE ; RDROP FALSE ;
\ Display the top of the stack as a word name if possible, or a number otherwise \ Display the top of the stack as a word name if possible, or a number otherwise
\ Words with zero-length names (e.g. from :NONAME) are displayed as numbers \ Words with zero-length names (e.g. from :NONAME) are displayed as numbers
: .W ( addr -- "<name>" | "<digits>" ) : .W ( addr -- "<name>" | "<digits>" )
DUP LOOKUP IF TYPE DROP ELSE . THEN ; DUP WORD? IF
\ Some kind of word; is the name zero-length (:NONAME)?
DUP >NAME DUP IF
\ Is the name hidden?
2 PICK HIDDEN? IF
"×" TYPE
ELSE
\ Does FIND with the same name fail to return the same word?
2DUP FIND AND-THEN 3 PICK = ELSE NIP NIP THEN 0= IF
"¤" TYPE
THEN
THEN
TYPE
DROP
ELSE
2DROP "∷" TYPE U.
THEN
ELSE
\ Not a word in the current search order or compilation word list
.
THEN ;
\ Display the first `u` words in the body of the given execution token with .W \ Display the first `u` words in the body of the given execution token with .W
: UNTHREAD ( xt u -- ) : UNTHREAD ( xt u -- )
@ -809,6 +946,7 @@ QUIT
DROP DROP
; ;
: BANNER "JumpForth version " TYPE VERSION . ", by Jesse McDonald" TYPE EOL ; : BANNER "JumpForth version " TYPE VERSION . ", by Jesse McDonald\nOK> " TYPE ;
BANNER BANNER
QUIT