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. :-)