1     Hi! I've decided to treat myself to some yak shaving for
     
2     a bit. First off, the "colon" and "word" terminology
     
3     makes this seem more like a traditional Forth than it
     
4     actually is. So I'm gonna rename some stuff...
     
5 
     6     First up, I'm going to change the term "word" to "def"
     
7     as in "definition". I was tempted to call them
     
8     "functions", but I don't think these inlined chunks of
     
9     code are actually functions.
    
10 
    11     Visibly, the ":" (colon) will change to "def".
    
12 
    13     Ten minutes later:
    
14 
    15 $ ./meow5 
    
16 
    17 def meow
    
18   "Meow!" say
    
19 ;
    
20 
    21 meow
    
22 Meow!
    
23 
    24     By the way, my little test script has been invaluable
    
25     for changes like this. I made a pass of renames and then
    
26     made sure I hadn't broken anything each time:
    
27 
    28 $ ./test.sh 
    
29 .............
    
30 Passed!
    
31 
    32     Okay, that was trivial. Now I want to improve the def
    
33     inspection experience by combining my "inspect" (gives
    
34     def stats) and "dump" (dumps a def's machine code) into
    
35     a single def because I always end up calling both of
    
36     them anyway.
    
37 
    38     And I also want to be able to call it like this:
    
39 
    40         inspect foo
    
41 
    42     instead of what I currently have to do:
    
43 
    44         "foo" find inspect
    
45 
    46     That turned out to also be trivial:
    
47 
    48 def m "meow" print ; 
    
49 inspect m
    
50 m: 43 bytes IMMEDIATE COMPILE 
    
51     e8 5 0 0 0 6d 65 6f 77 0 58 50 50 58 b9 0 0 0 0 80 3c 8 0 74 3 41 eb f7 51 5a 59 bb 1 0 0 0 b8 4 0 0 0 cd 80 
    
52 
    53 
    54     Now it's easy and handy to make a pretty good
    
55     illustration of a pure inlining concatenative language:
    
56 
    57 def ++ + + ;
    
58 
    59 10 10 10 ++ ps
    
60 30
    
61 
    62 inspect +
    
63 +: 5 bytes IMMEDIATE COMPILE
    
64     58 5b 1 d8 50
    
65 
    66 inspect ++
    
67 ++: 10 bytes IMMEDIATE COMPILE
    
68     58 5b 1 d8 50 58 5b 1 d8 50
    
69 
    70     Clearly "++" contains two copies of "+".
    
71 
    72     I'm also renaming "make_elf" to just "elf".
    
73 
    74     Here's what's fun about being able to write out a
    
75     program from any definition is that "compiling" from the
    
76     command line can look like this:
    
77 
    78 $ cat 5.meow
    
79 def meow
    
80     "Meow!\n" print
    
81 ;
    
82 
    83 def five-meows
    
84     meow
    
85     meow
    
86     meow
    
87     meow
    
88     meow
    
89     exit
    
90 ;
    
91 
    92 elf five-meows
    
93 
    94 $ ./meow5 < 5.meow
    
95 Wrote to "five-meows".
    
96 
    97 $ ./five-meows
    
98 Meow!
    
99 Meow!
   
100 Meow!
   
101 Meow!
   
102 Meow!
   
103 
   104     Gosh, this is really fun!
   
105 
   106     Something I discovered last night was that I don't have
   
107     a couple really obvious defs: dup and pop!
   
108 
   109     Well, *that* was easy:
   
110 
   111         STARTDEF popstack
   
112             pop eax
   
113         ENDDEF popstack, 'pop', (IMMEDIATE | COMPILE)
   
114 
   115         STARTDEF dupstack
   
116             pop eax
   
117             push eax
   
118             push eax
   
119         ENDDEF dupstack, 'dup', (IMMEDIATE | COMPILE)
   
120 
   121 $ ./meow5
   
122 1 2 3 4 ps
   
123 1 2 3 4
   
124 pop ps
   
125 1 2 3
   
126 dup ps
   
127 1 2 3 3
   
128 
   129     Now for conditionals!
   
130 
   131     I think the easiest one will be immediate-mode "if". I'm
   
132     going to have it just check the value on the stack (0
   
133     for false, 1 for true) and if will always either
   
134     continue for true or skip a single token of input for
   
135     false.
   
136 
   137         STARTDEF if
   
138             pop eax
   
139             test eax, eax ; zero?
   
140             jnz .continue_for_true
   
141             EAT_SPACES_CODE
   
142             GET_TOKEN_CODE
   
143             pop eax ; throw away token!
   
144         .continue_for_true:
   
145         ENDDEF if, '?', (IMMEDIATE)
   
146 
   147     Could it really be that easy?
   
148 
   149 def hi "Hello\n" print ;
   
150 0 ? hi
   
151 1 ? hi
   
152 Hello
   
153 
   154     Wow, yup. It really was that easy.
   
155 
   156     A compiled "if" wasn't nearly as easy, but I still got
   
157     it in a single evening. Here I'm making a silly
   
158     conditional plus def called "foo" with a lot of debug
   
159     printing turned on and comparing the compiled opcodes
   
160     for the conditional jump followed by the inlined "+"
   
161     def:
   
162 
   163 $ ./build.sh && ./meow5 
   
164 def foo ? + ;
   
165 tail of def:0804a0db
   
166 len of def:00000005
   
167 here:0804b1ac
   
168 len of def:00000005
   
169 here:0804b1b5
   
170 inlining:0804a0db
   
171 inspect +
   
172 +: 5 bytes IMMEDIATE COMPILE 
   
173     58 5b 1 d8 50 
   
174 inspect foo
   
175 foo: 14 bytes IMMEDIATE COMPILE 
   
176     58 85 c0 f 85 5 0 0 0 58 5b 1 d8 50 
   
177 1 1 1 foo ps
   
178 1 1 
   
179 1 1 0 foo ps
   
180 1 1 2 
   
181 
   182     Very cool!
   
183 
   184     The key part of making this work was remembering that it
   
185     needed to be set as a "RUNCOMP" def - so it would
   
186     actually *execute* in compile mode rather than be
   
187     compiled in as-is!
   
188 
   189     Now for looping!
   
190 
   191     The next thing is "loop?", which will be nearly identical
   
192     except it'll insert an unconditional jump after the def
   
193     that jumps back to perform the test again.
   
194 
   195         loop? <def>
   
196 
   197     Which should be fairly painless to use, I hope.
   
198 
   199     Hey, it works! I had some bugs, but nothing worth
   
200     writing up here.
   
201 
   202     Oh, and I'm now a HUGE fan of Evan Teran's EDB! Even
   
203     though it's a GUI program, it has now completely
   
204     replaced GDB for me with assembly language debugging. It
   
205     automatically halts on the entry instruction and
   
206     displays a disassembly - GDB makes this *brutally* hard
   
207     if you don't have a C program with debugging symbols.
   
208     EDB also shows graphical arrows to illustrate jumps,
   
209     multiple memory display areas, registers, etc. Pretty
   
210     intuitive to use.
   
211 
   212     https://github.com/eteran/edb-debugger
   
213 
   214     Oh, I also added comments! It's pretty much a copy of
   
215     "eat_spaces" for input, but the name of the def is "#"
   
216     to start the comment and it advances the input pointer
   
217     until it gets to a newline character. It's a RUNCOMP
   
218     def, so comments can also apepar in defs.
   
219 
   220     So here's my first loop test program:
   
221 
   222     ======================================================
   
223 
   224 $ cat loop.meow
   
225 # A meow5 program to test looping
   
226 
   227 # A chunk of code to be looped!
   
228 def foo
   
229     dup        # duplicate the number on the stack
   
230     printnum   # print it
   
231     " " print  # and a space
   
232     dec        # reduce the one still on the stack
   
233 
   234 # Another def that loops!
   
235 def foo-loop
   
236     loop? foo
   
237     "\n" print # put a newline after calling loop
   
238 ;
   
239 
   240 # Try some loops!
   
241 5 foo-loop
   
242 10 foo-loop
   
243 
   244     ======================================================
   
245 
   246     And let's run it:
   
247 
   248 $ ./meow5 < loop.meow
   
249 5 4 3 2 1
   
250 10 9 8 7 6 5 4 3 2 1
   
251 
   252     Heck yeah!
   
253 
   254     (Note that printnum is NOT position-independent code, so
   
255     exporting an ELF with this particular example explodes.)
   
256 
   257     Now I can do a loop version of the five meow printing
   
258     program:
   
259 
   260 
   261     ======================================================
   
262 
   263 $ cat five-loop.meow 
   
264 def meow
   
265     "Meow!\n" print
   
266 
   267     # decrement the stack each time or
   
268     # we'll end up with an infinite loop!
   
269     dec
   
270 ;
   
271 
   272 # Another def that loops five times and exits
   
273 def five-meows-loop
   
274     5 loop? meow
   
275     exit
   
276 ;
   
277 
   278 # Compile as an ELF executable
   
279 elf five-meows-loop
   
280 
   281     ======================================================
   
282 
   283     Let's try it!
   
284 
   285 $ ./meow5 <five-loop.meow 
   
286 Wrote to "five-meows-loop".
   
287 
   288 $ ./five-meows-loop 
   
289 Meow!
   
290 Meow!
   
291 Meow!
   
292 Meow!
   
293 Meow!
   
294 
   295     What's really cool about this is that the five-meows
   
296     program that has five copies of the 'meow' def is 317
   
297     bytes and this looping version is only 160 bytes!
   
298 
   299