1 Well, now that I can actually run jonesforth.f with this
2 interpreter port, it "only" remains to test the rest of
3 the words defined in Forth. Then I've got some cleaning
4 up to do in the assembly.
5
6 It looks like I'm roughly halfway through testing what
7 jonesforth.f provides. So there's quite a bit left.
8
9 Next up is CASE OF...ENDOF... ENDCASE which lets you
10 do different things based on comparison with a value on
11 the stack:
12
13 JONESFORTH VERSION 1
14 20643 CELLS REMAINING
15 OK
16
17 : foo CASE
18 1 OF ." One" ENDOF
19 2 OF ." Two" ENDOF
20 3 OF ." Three" ENDOF
21 ENDCASE ;
22
23 1 foo
24 One
25 2 foo
26 Two
27 3 foo
28 Three
29
30 That is, dare I say, quite readable compared to a lot of
31 the Forth we've seen so far.
32
33 Next is CFA>, which is "the opposite of >CFA." Let's see
34 about that. I'll use >CFA to get the code word pointer
35 address from the LATEST word address, then get back to
36 the original word address with CFA> and see if it's right:
37
38 HEX
39 LATEST @ DUP . ID.
40 957ED74 foo
41 LATEST @ >CFA DUP .
42 957ED7C
43 CFA> DUP . ID.
44 957ED74 foo
45
46 Of course, we can do this all in one go as well:
47
48 LATEST @ >CFA CFA> ID.
49 foo
50
51 What's wild is that >CFA can just do a little address
52 math and skip forward to the codeword. But CFA> has to
53 search the entire dictionary for the correct word.
54
55 I feel like you should be able to scan backward to the
56 word start. But I can see how it would be hard to be
57 entirely sure it's accurate (you could use the length
58 field as a kind of "checksum" for the name field, but
59 that wouldn't be a *guarantee*).
60
61 Oooooh, the next one is bound to be a new favorite: SEE
62 decompiles a word!
63
64 SEE foo
65 : foo 1 OVER = 0BRANCH ( 20 ) DROP S" One" TELL BRANCH ( 74 ) 2 OVER = 0BRANCH
66 ( 20 ) DROP S" Two" TELL BRANCH ( 40 ) 3 OVER = 0BRANCH ( 24 ) DROP S" Three"
67 TELL BRANCH ( 8 ) DROP ;
68
69 That's awesome! I love how it prints the strings and
70 everthing.
71
72 The next thing are anonymous "no name" words. When you
73 define them with :NONAME, they leave the address of
74 their codeword on the stack. In Forth terminology, the
75 address of a codeword is an "execution token". In the
76 21st Century, we'd probably call them pointers.
77
78 Anyway, you can store an execution token like any other
79 value. And EXECUTE calls one:
80
81 :NONAME ." I have no name: " TELL CR ;
82 VALUE noname
83 noname .
84 160980384
85 ." Hello" noname EXECUTE
86 HelloI have no name: .
87 S" Hello" noname EXECUTE
88 I have no name: Hello
89
90 Next we have THROW and CATCH. I was not expecting
91 exception handling!
92
93 The THROW statement is easy to use - just give it a
94 number to throw as the exception.
95
96 But CATCH needs some very specific stuff:
97
98 : <word2> ['] <word1> CATCH IF ... THEN ;
99
100 The ['] primitive gets the execution token of a word
101 (:NONAME uses it).
102
103 : foo ." Foo! " 13 = IF ." Bah!" 5 THROW THEN ." Done." ;
104 12 foo
105 Foo! Done.
106 13 foo
107 Foo! Bah!UNCAUGHT THROW 5
108
109 : test-foo ['] foo CATCH ?DUP IF ." Foo threw " . DROP THEN ." Test done." ;
110 12 test-foo
111 Foo! Done. Test done.
112 13 test-foo
113 Foo! Bah! Foo threw 5 Test done.
114
115 i spent another night reading through the implementation
116 of THROW. It's funny how *using* CATCH and THROW is
117 inversely proportional to the complexity of those two
118 words.
119
120 This is the first time something really interesting is
121 done with the return stack.
122
123 This is also an excellent example of a language feature
124 that seems like more trouble than it's worth in a small
125 demonstration. But when you actually need a feature like
126 this in a decent-sized codebase, it will seem trivial
127 compared to the alternatives.
128
129 Having said that, reading an implementation like THROW
130 is just *nuts*. Cool how it works, though.
131
132 Next is another one that takes advantage of the return
133 stack: PRINT-STACK-TRACE.
134
135 : foo PRINT-STACK-TRACE ;
136 foo
137 foo+0
138 : bar foo ;
139 : baz bar ;
140 baz
141 foo+0 bar+0 baz+0
142
143 Beautiful.
144
145 Then JONESFORTH has support for reading the commandline
146 arguments, which is very cool.
147
148 It's easy enough to use ARGC and ARGV, but I'm getting
149 to the point where the usability of bare Linux input for
150 the Forth "REPL" is pretty painful. I must have re-typed
151 my BEGIN WHILE REPEAT loop a dozen times.
152
153 So I'm doing two things:
154
155 1. Using CAPSLOCK while typing Forth commands.
156 2. Installed "rlwrap" (Readline wrapper).
157
158 rlwrap is super awesome and easy to use. I'll be darned
159 if I can figure out why `apk search rlwrap` doesn't work
160 in Alpine Linux. It shows up as a community package when
161 I do a web search.
162
163 But whatever, that's what source is for:
164
165 $ doas apk add readline-dev
166 $ lynx github.com...
167 $ tar -xf rlwrap...tar.gz
168 $ cd rlwrap...
169 $ ./configure
170 $ make
171 $ doas make install
172
173 Now I've added this alias:
174
175 alias f='rlwrap /home/dave/nasmjf/nasmjf'
176
177 And what a relief! I can use arrow keys for editing and
178 history, etc.
179
180 eeepc:~/nasmjf$ f
181 JONESFORTH VERSION 1
182 20643 CELLS REMAINING
183 OK
184 ARGC .
185 1
186 0 ARGV TELL
187 /home/dave/nasmjf/nasmjf
188
189 Again, that's my alias 'f' providing the full path to
190 the nasmjf executable we're seeing there as the 0th
191 argument.
192
193 Now for the simple loop it took at least a dozen tries
194 to get right (often due to simple typos and some of them
195 causing nasmjf to segfault!) and now I am *so* grateful
196 that rlwrap gives me command history.
197
198 eeepc:~/nasmjf$ f foo bar
199 JONESFORTH VERSION 1
200 20643 CELLS REMAINING
201 OK
202
203 ARGC .
204 3
205
206 : PRINT-ARGS 0 BEGIN DUP ARGC < WHILE DUP DUP . ARGV TELL CR 1+ REPEAT ;
207 PRINT-ARGS
208 0 /home/dave/nasmjf/nasmjf
209 1 foo
210 2 bar
211
212 Yes!
213
214 And by the way, I have no doubt I'll get better at it,
215 but writing that loop felt way harder than it should
216 have been. The way you have to keep DUPing because
217 even comparisons eat the values on the stack...
218
219 I get it. But I'm not sure I like it.
220
221 Also, having capslock on makes my GNU screen shortcuts
222 and vim both go crazy when i forget to toggle it off
223 when i'm done typing Forth. :-(
224
225 But rlwrap is the best. I *highly* recommend it for use
226 with this or any other application that doesn't have
227 command history, etc.
228
229 Related to arguments (because Linux leaves it all in our
230 application's memory in the stack memory space,
231 evidently) are the environment variables.
232
233 JONESFORTH defines ENVIRON to get the address. Also the
234 utility STRLEN to get the length of null-terminated
235 strings, a.k.a. "C strings".
236
237 ENVIRON @ DUP STRLEN TELL
238 SHELL=/bin/bash
239
240 Unlike the result from ARGV, ENVIRON just gives us the
241 address of the first item. So I had to DUP and STRLEN to
242 get the string ready for printing with TELL.
243
244 The next item is 4 bytes after that and so on.
245
246 I guess you could write a word that gets the number of
247 environment variables available and another that gets
248 one from a particular position.
249
250 I started writing those words, but it was late and I was
251 tired. Eventually, I just wanted to see more, so I
252 printed out a big chunk of memory where args and
253 environment are stored:
254
255 0 ARGV DROP 128 DUMP
256 BFECBA8E 2E 2F 6E 61 73 6D 6A 66 0 66 6F 6F 0 62 61 72 ./nasmjf.foo.bar
257 BFECBA9E 0 53 48 45 4C 4C 3D 2F 62 69 6E 2F 62 61 73 68 .SHELL=/bin/bash
258 BFECBAAE 0 43 48 41 52 53 45 54 3D 55 54 46 2D 38 0 54 .CHARSET=UTF-8.T
259 BFECBABE 45 52 4D 43 41 50 3D 53 43 7C 73 63 72 65 65 6E ERMCAP=SC|screen
260 BFECBACE 7C 56 54 20 31 30 30 2F 41 4E 53 49 20 58 33 2E |VT 100/ANSI X3.
261 BFECBADE 36 34 20 76 69 72 74 75 61 6C 20 74 65 72 6D 69 64 virtual termi
262 BFECBAEE 6E 61 6C 3A 44 4F 3D 5C 45 5B 25 64 42 3A 4C 45 nal:DO=\E[%dB:LE
263 BFECBAFE 3D 5C 45 5B 25 64 44 3A 52 49 3D 5C 45 5B 25 64 =\E[%dD:RI=\E[%d
264
265 I'll end this particular log file with an appropriate
266 sign-off with another new word definition:
267
268 JONESFORTH VERSION 1
269 20643 CELLS REMAINING
270 OK
271 BYE