Added DO WHILE REPEAT, as well as . and .s
[forth.jl.git] / src / lib.4th
1 ' 1+ , : / /MOD SWAP DROP ;
2 : MOD /MOD DROP ;
3
4 : '\n' 10 ;
5 : BL 32 ;
6
7 : CR '\n' emit ;
8 : SPACE BL emit ;
9
10 : NEGATE 0 swap - ;
11
12 : TRUE -1 ;
13 : FALSE 0 ;
14 : NOT 0= ;
15
16 : CELLS ; \ Allow for slightly more portable code
17
18 : DEPTH PSP@ PSP0 @ - ;
19
20 : LITERAL IMMEDIATE ' LIT , , ;
21
22 : ':' [ CHAR : ] LITERAL ;
23 : ';' [ CHAR ; ] LITERAL ;
24 : '(' [ CHAR ( ] LITERAL ;
25 : ')' [ CHAR ) ] LITERAL ;
26 : '<' [ CHAR < ] LITERAL ;
27 : '>' [ CHAR > ] LITERAL ;
28 : '"' [ CHAR " ] LITERAL ;
29 : 'A' [ CHAR A ] LITERAL ;
30 : '0' [ CHAR 0 ] LITERAL ;
31 : '-' [ CHAR - ] LITERAL ;
32 : '.' [ CHAR . ] LITERAL ;
33
34 : [COMPILE] IMMEDIATE
35         WORD            \ get the next word
36         FIND            \ find it in the dictionary
37         >CFA            \ get its codeword
38         ,               \ and compile that
39 ;
40
41 : RECURSE IMMEDIATE
42         LATEST @        \ LATEST points to the word being compiled at the moment
43         >CFA            \ get the codeword
44         ,               \ compile it
45 ;
46
47 \ CONTROL STRUCTURES ----------------------------------------------------------------------
48
49 : IF IMMEDIATE
50         ' 0BRANCH ,     \ compile 0BRANCH
51         HERE @          \ save location of the offset on the stack
52         0 ,             \ compile a dummy offset
53 ;
54
55 : THEN IMMEDIATE
56         DUP
57         HERE @ SWAP -   \ calculate the offset from the address saved on the stack
58         SWAP !          \ store the offset in the back-filled location
59 ;
60
61 : ELSE IMMEDIATE
62         ' BRANCH ,      \ definite branch to just over the false-part
63         HERE @          \ save location of the offset on the stack
64         0 ,             \ compile a dummy offset
65         SWAP            \ now back-fill the original (IF) offset
66         DUP             \ same as for THEN word above
67         HERE @ SWAP -
68         SWAP !
69 ;
70
71 : BEGIN IMMEDIATE
72         HERE @          \ save location on the stack
73 ;
74
75 : UNTIL IMMEDIATE
76         ' 0BRANCH ,     \ compile 0BRANCH
77         HERE @ -        \ calculate the offset from the address saved on the stack
78         ,               \ compile the offset here
79 ;
80
81 : AGAIN IMMEDIATE
82         ' BRANCH ,      \ compile BRANCH
83         HERE @ -        \ calculate the offset back
84         ,               \ compile the offset here
85 ;
86
87 : WHILE IMMEDIATE
88         ' 0BRANCH ,     \ compile 0BRANCH
89         HERE @          \ save location of the offset2 on the stack
90         0 ,             \ compile a dummy offset2
91 ;
92
93 : REPEAT IMMEDIATE
94         ' BRANCH ,      \ compile BRANCH
95         SWAP            \ get the original offset (from BEGIN)
96         HERE @ - ,      \ and compile it after BRANCH
97         DUP
98         HERE @ SWAP -   \ calculate the offset2
99         SWAP !          \ and back-fill it in the original location
100 ;
101
102 : UNLESS IMMEDIATE
103         ' NOT ,         \ compile NOT (to reverse the test)
104         [COMPILE] IF    \ continue by calling the normal IF
105 ;
106
107 : DO IMMEDIATE
108         ' >R , ' >R ,
109         HERE @
110 ;
111
112 : LOOP IMMEDIATE
113         ' R> , ' R> , ' 1+ , ' 2DUP , ' - ,
114         ' SWAP , ' >R , ' SWAP , ' >R ,
115         ' 0<= , ' 0BRANCH ,
116         HERE @ - ,
117         ' RDROP , ' RDROP ,
118 ;
119
120
121 \ COMMENTS ----------------------------------------------------------------------
122
123 : ( IMMEDIATE
124         1               \ allowed nested parens by keeping track of depth
125         BEGIN
126                 KEY             \ read next character
127                 DUP '(' = IF    \ open paren?
128                         DROP            \ drop the open paren
129                         1+              \ depth increases
130                 ELSE
131                         ')' = IF        \ close paren?
132                                 1-              \ depth decreases
133                         THEN
134                 THEN
135         DUP 0= UNTIL            \ continue until we reach matching close paren, depth 0
136         DROP            \ drop the depth counter
137 ;
138
139 ( Some more complicated stack examples, showing the stack notation. )
140 : NIP ( x y -- y ) SWAP DROP ;
141 : TUCK ( x y -- y x y ) DUP ROT ;
142 : PICK ( x_u ... x_1 x_0 u -- x_u ... x_1 x_0 x_u )
143         1+              ( add one because of 'u' on the stack )
144         PSP@ SWAP -     ( add to the stack pointer )
145         @               ( and fetch )
146 ;
147
148 ( With the looping constructs, we can now write SPACES, which writes n spaces to stdout. )
149 : SPACES        ( n -- )
150         BEGIN
151                 DUP 0>          ( while n > 0 )
152         WHILE
153                 SPACE           ( print a space )
154                 1-              ( until we count down to 0 )
155         REPEAT
156         DROP
157 ;
158
159 ( Standard words for manipulating BASE. )
160 : DECIMAL ( -- ) 10 BASE ! ;
161 : HEX ( -- ) 16 BASE ! ;
162
163 ( Compute absolute value. )
164 : ABS           ( n -- m)
165         dup 0< if
166                 negate
167         then
168 ;
169
170 ( PRINTING NUMBERS ---------------------------------------------------------------------- )
171
172 ( This is the underlying recursive definition of U. )
173 : U.            ( u -- )
174         BASE @ /MOD     ( width rem quot )
175         ?DUP IF                 ( if quotient <> 0 then )
176                 RECURSE         ( print the quotient )
177         THEN
178
179         ( print the remainder )
180         DUP 10 < IF
181                 '0'             ( decimal digits 0..9 )
182         ELSE
183                 10 -            ( hex and beyond digits A..Z )
184                 'A'
185         THEN
186         +
187         EMIT
188 ;
189
190 ( This word returns the width (in characters) of an unsigned number in the current base )
191 : UWIDTH        ( u -- width )
192         BASE @ /        ( rem quot )
193         ?DUP IF         ( if quotient <> 0 then )
194                 RECURSE 1+      ( return 1+recursive call )
195         ELSE
196                 1               ( return 1 )
197         THEN
198 ;
199
200 : U.R           ( u width -- )
201         SWAP            ( width u )
202         DUP             ( width u u )
203         UWIDTH          ( width u uwidth )
204         -ROT            ( u uwidth width )
205         SWAP -          ( u width-uwidth )
206         ( At this point if the requested width is narrower, we'll have a negative number on the stack.
207           Otherwise the number on the stack is the number of spaces to print.  But SPACES won't print
208           a negative number of spaces anyway, so it's now safe to call SPACES ... )
209         SPACES
210         ( ... and then call the underlying implementation of U. )
211         U.
212 ;
213
214 : .R            ( n width -- )
215         SWAP            ( width n )
216         DUP 0< IF
217                 NEGATE          ( width u )
218                 1               ( save a flag to remember that it was negative | width n 1 )
219                 ROT             ( 1 width u )
220                 SWAP            ( 1 u width )
221                 1-              ( 1 u width-1 )
222         ELSE
223                 0               ( width u 0 )
224                 ROT             ( 0 width u )
225                 SWAP            ( 0 u width )
226         THEN
227         SWAP            ( flag width u )
228         DUP             ( flag width u u )
229         UWIDTH          ( flag width u uwidth )
230         -ROT            ( flag u uwidth width )
231         SWAP -          ( flag u width-uwidth )
232
233         SPACES          ( flag u )
234         SWAP            ( u flag )
235
236         IF                      ( was it negative? print the - character )
237                 '-' EMIT
238         THEN
239
240         U.
241 ;
242
243 : U. U. SPACE ;
244
245 : . 0 .R SPACE ;
246
247 : .S            ( -- )
248         '<' EMIT DEPTH U. '>' EMIT SPACE
249         PSP0 @ 1+
250         BEGIN
251                 DUP PSP@ 2 - <=
252         WHILE
253                 DUP @ .
254                 1+
255         REPEAT
256         DROP
257 ;
258
259 ( ? fetches the integer at an address and prints it. )
260 : ? ( addr -- ) @ . ;
261
262 ( c a b WITHIN returns true if a <= c and c < b )
263 : WITHIN
264         ROT             ( b c a )
265         OVER            ( b c a c )
266         <= IF
267                 > IF            ( b c -- )
268                         TRUE
269                 ELSE
270                         FALSE
271                 THEN
272         ELSE
273                 2DROP           ( b c -- )
274                 FALSE
275         THEN
276 ;
277