1 I'm back! It's been over a month and I had to stop just
2 when it was getting exiciting. :-(
3
4 Anyway, I can now read any amount of jonesforth.f upon
5 starting the interpreter. So it's easy to test chunks
6 of Forth implementation by just incrementing the temporary
7 constant I've put in the assembly:
8
9 __lines_of_jf_to_read
10
11 But that doesn't mean it will be *easy*. The rest of
12 the FORTH implementation is implemented in FORTH, so
13 plenty of mind-bending lies ahead.
14
15 Setting the lines to read to 97, we now have these
16 10 new definitions to try out:
17
18 LITERAL ':' ';' '(' ')' '"' 'A' '0' '-' '.'
19
20 Yes, the quotes are part of the word names (it is 'A' not A).
21
22 First, LITERAL, sounds a lot like LIT, right?
23
24 Here's the entire definition:
25
26 \ LITERAL takes whatever is on the stack and compiles LIT <foo>
27 : LITERAL IMMEDIATE
28 ' LIT , \ compile LIT
29 , \ compile the literal itself (from the stack)
30 ;
31
32 I don't know ahout you, but I'm already starting to get a bit
33 rusty after my hitatus. So I'm going to need to remind myself
34 what these separate words mean.
35
36 IMMEDIATE Means LITERAL will always be executed as soon
37 as it's encountered even if we're in compile mode.
38
39 ' (TICK) Matches the next "word" of input with a compiled
40 word and returns its address.
41
42 LIT Pushes the next value onto the stack.
43
44 , (COMMA) Puts the currently-pushed value from the stack to
45 the position pointed to by HERE.
46
47 None of ' LIT , are immediate mode words, so they're being compiled
48 into LITERAL.
49
50 All together, this means that we
51
52 * get the address of the next word
53 * push that address on the stack
54 * write the address at HERE ("compile it")
55 * write the next address from the stack ("compile it")
56
57 Well, I can't make heads or tails of that functionality on its
58 own, but the next words make use of it. Jones thoroughly breaks
59 the first of them down in a big comment:
60
61 \ Now we can use [ and ] to insert literals which are calculated at
62 \ compile time. (Recall that [ and ] are the FORTH words which switch
63 \ into and out of immediate mode.) Within definitions, use [ ... ]
64 \ LITERAL anywhere that '...' is a constant expression which you would
65 \ rather only compute once (at compile time, rather than calculating it
66 \ each time your word runs).
67 : ':'
68 [ \ go into immediate mode (temporarily)
69 CHAR : \ push the number 58 (ASCII code of colon) on the parameter stack
70 ] \ go back to compile mode
71 LITERAL \ compile LIT 58 as the definition of ':' word
72 ;
73
74 Followed by the rest of the definitions:
75
76 \ A few more character constants defined the same way as above.
77 : ';' [ CHAR ; ] LITERAL ;
78 : '(' [ CHAR ( ] LITERAL ;
79 : ')' [ CHAR ) ] LITERAL ;
80 : '"' [ CHAR " ] LITERAL ;
81 : 'A' [ CHAR A ] LITERAL ;
82 : '0' [ CHAR 0 ] LITERAL ;
83 : '-' [ CHAR - ] LITERAL ;
84 : '.' [ CHAR . ] LITERAL ;
85
86 These are all crazy to look at coming from "normal" programming
87 languages because of the single quotes in the word names!
88
89 Anyway, taken all together, we get:
90
91 * Compile a word called ':'
92 * Switch to immediate mode
93 * CHAR takes the next character and puts on stack
94 * : is next character
95 * Switch back to compile mode, : is on stack
96 * LITERAL:
97 * gets the address of the next word
98 * push that address on the stack
99 * write the address at HERE ("compile it")
100 * write the next address from the stack ("compile it")
101
102 Or, more simply: we put the ASCII value of the character on the
103 stack, and LITERAL compiles the LIT word followed by the character
104 from the stack. Thus, we end up with a compiled word that will
105 perform a LIT + character (puts that character on the stack).
106
107 Okay, got it. Let's see it in action with one of the defined words:
108
109 'A' .
110 65
111 'A' EMIT
112 A
113 ':' EMIT
114 :
115
116 Now I'll try making my own word that uses LITERAL:
117
118 : Z [ CHAR z LITERAL ] ;
119 Z EMIT
120 z
121
122 And I can compile that into another word:
123
124 : emitz Z EMIT ;
125 emitz
126 z
127
128 Yay, I finally understand this! I kept trying to "get it" late
129 at night and it just wasn't clicking. What made this so hard
130 to understand has nothing to do with the stack, it's the
131 temporal nature of the compile-time vs run-time execution of
132 these low-level compiler words in FORTH.
133
134 Some of it runs "now" and some of it runs "later". For certain
135 definitions of now and later. Thankfully, it's all much
136 more comprehensible on a weekend morning.
137
138 Well, this log has now spanned two months. So even though I
139 didn't cover a TON of ground here, I'm gonna start a new log
140 for the next installment. :-)