feat(v4.0.0): reading a line and splitting it into words and numbers
The first layer of the compiler capsule, on the host node: TIB >IN SPAN SOURCE BL EXPECT QUERY WORD ENCLOSE CONVERT NUMBER and the comment words. The definitions are text, v4/capsule/core.v4 (the core words they rest on) and v4/capsule/input.v4, assembled by the text assembler, which can now read a file. EXPECT, QUERY, WORD and ENCLOSE behave as v3's. CONVERT and NUMBER are FORTH-79 (ruled 2026-10-04): any BASE, a double in the standard order, and NUMBER returns a signed double or sets NODE-ERROR. Executed at both cell widths against C and against values recorded from the v3 binary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
31fcdcd0a3
commit
443439c3d2
@@ -0,0 +1,142 @@
|
||||
\ input.v4 -- reading a line and splitting it into words and numbers.
|
||||
\
|
||||
\ DECOMPOSITION.md 5.9: TIB >IN SPAN SOURCE BL EXPECT QUERY WORD ENCLOSE
|
||||
\ CONVERT NUMBER, and the comment words ( and \ of 5.15 (named PAREN and
|
||||
\ BACKSLASH here, since the text assembler reads those two characters as its
|
||||
\ own comments; the dictionary will give them their names). Part of the compiler
|
||||
\ capsule: it runs on the host node. Rests on core.v4.
|
||||
\
|
||||
\ Constants the loader supplies:
|
||||
\ TIB byte address of the text input buffer, #TIB bytes long
|
||||
\ #TIB 1025: 1024 characters and a terminating zero, as v3
|
||||
\ >IN word address of the variable: offset in TIB of the next character
|
||||
\ SPAN word address of the variable: how many characters the last
|
||||
\ EXPECT or QUERY read
|
||||
\ WBUF byte address of WORD's buffer, 64 bytes
|
||||
\ (P) word address of six cells of scratch for this file
|
||||
\ TIB >IN SPAN and BL are in-line: written in a definition they are literals.
|
||||
|
||||
macro BL 32 endmacro
|
||||
|
||||
\ ( -- baddr u ) the input buffer and how much of it is in use
|
||||
: SOURCE TIB SPAN a! @ -if P drop 0 P: ;
|
||||
|
||||
\ ( baddr u -- ) read a line into u bytes at baddr, as v3's fgets did: at
|
||||
\ most u-1 characters, stopping after a new-line, which is taken but not
|
||||
\ stored; then a zero. A line of u-1 characters or more leaves what did not
|
||||
\ fit, its new-line included, to be read next. SPAN is the number of characters stored. With u = 0
|
||||
\ nothing is read or stored; u < 0 does nothing and sets NODE-ERROR.
|
||||
: EXPECT
|
||||
-if OK drop drop NODE-ERROR b! -1 !b ;
|
||||
OK: if ZERO
|
||||
over push \ p n R: start
|
||||
L: -1 + if END \ room for the zero only
|
||||
push KEY dup -10 + if NL \ p c x R: start n
|
||||
drop over C! 1 + pop jump L
|
||||
NL: drop drop pop
|
||||
END: drop 0 over C! \ p
|
||||
pop - SPAN a! ! ;
|
||||
ZERO: drop drop 0 SPAN a! ! ;
|
||||
|
||||
\ ( -- ) read a line into TIB and start at its beginning
|
||||
: QUERY TIB #TIB EXPECT 0 >IN a! ! ;
|
||||
|
||||
\ ---- WORD ------------------------------------------------------------------
|
||||
\ (P)+0 the delimiter, (P)+1 the length of the text in TIB.
|
||||
macro (WDELIM) (P) endmacro
|
||||
macro (WLEN) (P) 1 + endmacro
|
||||
|
||||
: (LEFT) ( i -- i d ) dup (WLEN) a! @ - ; \ d < 0 while i is inside the text
|
||||
: (CH) ( i -- i x ) dup TIB + C@ (WDELIM) a! @ xor ; \ x = 0 at a delimiter
|
||||
: (SKIP) ( i -- i' ) \ past delimiters
|
||||
L: (LEFT) -if E drop (CH) if S drop ;
|
||||
S: drop 1 + jump L
|
||||
E: drop ;
|
||||
: (SCAN) ( i -- i' ) \ up to a delimiter
|
||||
L: (LEFT) -if E drop (CH) if E drop 1 + jump L
|
||||
E: drop ;
|
||||
|
||||
\ ( c -- baddr ) the next word of TIB delimited by c, as a counted string
|
||||
\ in WBUF with a zero after it. Leading delimiters are skipped; so, as in
|
||||
\ v3, are all those that follow the word. A word longer than 62 characters
|
||||
\ is cut to 62. At the end of the text the count is 0.
|
||||
: WORD
|
||||
255 and (WDELIM) a! !
|
||||
SPAN a! @ -if A drop 0 A: (WLEN) a! !
|
||||
>IN a! @ -if B drop 0 B:
|
||||
(SKIP) dup push (SCAN) \ end R: start
|
||||
pop over over - \ end start len
|
||||
dup -63 + -if CAP drop jump FITS CAP: drop drop 62 FITS:
|
||||
dup WBUF C!
|
||||
dup push push TIB + WBUF 1 + pop CMOVE \ end R: len
|
||||
0 pop WBUF + 1 + C!
|
||||
(SKIP) >IN a! ! WBUF ;
|
||||
|
||||
\ ---- ENCLOSE ---------------------------------------------------------------
|
||||
\ (P)+0 the delimiter, (P)+1 the string's address.
|
||||
: (E?) ( i -- i k ) \ k: 0 at the end of the string, 1 at a delimiter, else 2
|
||||
dup (WLEN) a! @ + C@ if END (WDELIM) a! @ xor if DEL drop 2 ;
|
||||
DEL: drop 1 ;
|
||||
END: drop 0 ;
|
||||
: (ESKIP) ( i -- i' ) L: (E?) -1 + if S drop ; S: drop 1 + jump L
|
||||
: (ESCAN) ( i -- i' ) L: (E?) -2 + if S drop ; S: drop 1 + jump L
|
||||
|
||||
\ ( baddr c -- baddr n1 n2 n3 ) in the zero-terminated string at baddr:
|
||||
\ n1 the offset of the first character that is not c, n2 the offset of the
|
||||
\ first c after it, n3 the offset of the first character after that run of c.
|
||||
: ENCLOSE
|
||||
255 and (WDELIM) a! ! dup (WLEN) a! !
|
||||
0 (ESKIP) dup (ESCAN) dup (ESKIP) ;
|
||||
|
||||
\ ---- CONVERT and NUMBER ----------------------------------------------------
|
||||
\ ( c -- n ) the value of the digit c, 0 .. 35, in either case; -1 if none
|
||||
: (DIGIT)
|
||||
dup -48 + -if GE0 drop drop -1 ;
|
||||
GE0: drop dup -58 + -if GT9 drop -48 + ;
|
||||
GT9: drop dup -65 + -if GEA drop drop -1 ;
|
||||
GEA: drop dup -91 + -if GTZ drop -55 + ;
|
||||
GTZ: drop dup -97 + -if GEa drop drop -1 ;
|
||||
GEa: drop dup -123 + -if GTz drop -87 + ;
|
||||
GTz: drop drop -1 ;
|
||||
|
||||
\ ( d1 baddr1 -- d2 baddr2 ) FORTH-79: the characters from baddr1 + 1 on are
|
||||
\ taken as digits in the current BASE, each accumulated into the double after
|
||||
\ it is multiplied by BASE, until one is not a digit; baddr2 is its address.
|
||||
\ (P)+2 holds the digit while the double is multiplied and (P)+5 the address,
|
||||
\ so nothing waits on the return stack across the calls.
|
||||
: CONVERT
|
||||
L: 1 + dup (P) 5 + a! ! C@ (DIGIT) \ lo hi n
|
||||
-if DIG drop (P) 5 + a! @ ;
|
||||
DIG: dup (BASE) - -if BIG
|
||||
drop (P) 2 + a! ! \ lo hi
|
||||
(BASE) UM* drop \ lo hi*base
|
||||
SWAP (BASE) UM* \ hi*base plo phi
|
||||
push SWAP pop + \ plo hi'
|
||||
(P) 2 + a! @ 0 D+
|
||||
(P) 5 + a! @ jump L
|
||||
BIG: drop drop (P) 5 + a! @ ;
|
||||
|
||||
\ ( baddr -- d ) FORTH-79: the counted string at baddr as a signed double in
|
||||
\ the current BASE; it may begin with a minus sign. If it is empty, is only
|
||||
\ a sign, or holds anything that is not a digit, the result is 0 and
|
||||
\ NODE-ERROR is set. The character after the string must not be a digit
|
||||
\ (WORD puts a zero there); if it is, that too is reported as an error. (P)+3 the address of its last character, (P)+4 the sign.
|
||||
: NUMBER
|
||||
dup C@ if ERR2
|
||||
over + (P) 3 + a! ! \ baddr
|
||||
dup 1 + C@ -45 + if NEG
|
||||
drop 0 (P) 4 + a! ! jump GO
|
||||
NEG: drop -1 (P) 4 + a! ! 1 +
|
||||
dup (P) 3 + a! @ xor if ERR2 drop
|
||||
GO: push 0 0 pop CONVERT \ lo hi baddr2
|
||||
(P) 3 + a! @ 1 + xor if FINE
|
||||
drop drop drop jump ERR0
|
||||
FINE: drop (P) 4 + a! @ if POS drop jump DNEGATE
|
||||
POS: drop ;
|
||||
ERR2: drop drop
|
||||
ERR0: NODE-ERROR b! -1 !b 0 0 ;
|
||||
|
||||
\ ---- comments (5.15) -------------------------------------------------------
|
||||
\ PAREN skips to the closing parenthesis; BACKSLASH skips the rest of the line.
|
||||
: PAREN 41 WORD drop ;
|
||||
: BACKSLASH SPAN a! @ >IN a! ! ;
|
||||
Reference in New Issue
Block a user