1 # Meow5: "Meow. Meow. Meow. Meow. Meow."
     
2 
     3 <img src="raw/meow5cat.svg" alt="SVG meow5 kitty cat logo" align="right">
     
4 
     5 **Update 2023-11-21** Meow5 is done!
     
6 <a href="https://www.ratfactor.com/meow5/done">Read my conclusion here!</a>
     
7 
     8 Meow5 is a stack-based pure inlining concatenative programming language.
     
9 
    10 Running Meow5 interactively looks like this:
    
11 
    12     5 5 +
    
13     "The answer is $." print$
    
14     The answer is 10.
    
15 
    16 In the above example, the first line puts two fives on the
    
17 stack and adds them.
    
18 
    19 The second line prints the answer. (The "$" character pops a number from the stack and includes it in the printed string.)
    
20 
    21 Now we can see that 5 + 5 is 10.
    
22 
    23 A block of code can be given a name and is called a "def"
    
24 (short for definition). Here's one:
    
25 
    26     def meow
    
27         "Meow!" print
    
28     ;
    
29 
    30     meow
    
31     Meow!
    
32 
    33 Defs can include other defs. Here's a silly example:
    
34 
    35     def five 5 ;
    
36     def plus + ;
    
37     def ten five five plus ;
    
38 
    39     ten "Ten is $" print$
    
40     Ten is 10
    
41 
    42 Meow5 can write *any* def as a stand-alone 32-bit Linux ELF
    
43 executable. (But not all defs are position-independent at
    
44 this time, so some executables will segfault!)  Here's an
    
45 example session:
    
46 
    47     $ ./meow5
    
48 
    49     def forty-two
    
50         42 exit
    
51     ;
    
52 
    53     elf forty-two
    
54     Wrote to "forty-two".
    
55 
    56     $ ./forty-two
    
57     $ echo $?
    
58     42
    
59 
    60 Note that `$?` contains the exit code of the previous command.
    
61 
    62 The file `forty-two` is a 97-byte program that will run on
    
63 any Linux system!
    
64 
    65 The canonical Meow5 program writes five meows. Here is a
    
66 self-contained meow file:
    
67 
    68     $ cat 5.meow 
    
69     def meow
    
70         "Meow!\n" print
    
71     ;
    
72 
    73     def five-meows
    
74         meow
    
75         meow
    
76         meow
    
77         meow
    
78         meow
    
79         exit
    
80     ;
    
81 
    82     elf five-meows
    
83 
    84 That last line tells the interpreter to write out the
    
85 five-meows def as a stand-alone executable. Let's see what
    
86 happens when we redirect this file to the interpreter:
    
87 
    88     $ ./meow5 < 5.meow
    
89     Wrote to "five-meows".
    
90 
    91 It wrote a 317 byte executable named `five-meows`. Let's run it:
    
92 
    93     $ ./five-meows
    
94     Meow!
    
95     Meow!
    
96     Meow!
    
97     Meow!
    
98     Meow!
    
99 
   100 Now I can have meowing in my terminal any time I want!
   
101 
   102 ### Now with loops!
   
103 
   104 Ah, but now it gets better! I've added ifs and loops.
   
105 A looped version of the five-meows program is much
   
106 more efficient with space. Here's `five-loop.meow`:
   
107 
   108     def meow
   
109         "Meow!\n" print
   
110 
   111         # decrement the stack each time or
   
112         # we'll end up with an infinite loop!
   
113         dec
   
114     ;
   
115 
   116     # Another def that loops!
   
117     def five-meows-loop
   
118         5 loop? meow
   
119         exit
   
120     ;
   
121 
   122     # Write an executable :-)
   
123     elf five-meows-loop
   
124 
   125 It works exactly like the five copy inline version:
   
126 
   127     $ ./meow5 <five-loop.meow 
   
128     Wrote to "five-meows-loop".
   
129 
   130     $ ./five-meows-loop 
   
131     Meow!
   
132     Meow!
   
133     Meow!
   
134     Meow!
   
135     Meow!
   
136 
   137 Except this executable is a mere **160 bytes**. Cool!
   
138 
   139 
   140 ## What is Meow5? (and what is "pure inlining"?)
   
141 
   142 A Forth-like language that is conCATenative in two ways:
   
143 
   144 1. Concatenative data flow (traditional)
   
145 2. Concatenated machine code (weird)
   
146 
   147 There's a lot of information on the Web about concatenative
   
148 programming in the first, traditional sense. But that second
   
149 part is what's unique about Meow5.
   
150 
   151 Luckily, really easy to explain how this works, thanks to
   
152 Meow5's introspection abilities:
   
153 
   154 Using `inspect`, we can view the machine code of any def:
   
155 
   156     inspect +
   
157     +: 5 bytes IMMEDIATE COMPILE
   
158         58 5b 1 d8 50
   
159 
   160 We can see that the `+` def contains five bytes of machine
   
161 code.
   
162 
   163 By the way, that machine code disassembles into this assembly
   
164 language:
   
165 
   166     pop eax
   
167     pop ebx
   
168     add eax, ebx
   
169     push eax
   
170 
   171 If we define a new def that uses an existing def, the
   
172 machine code simply gets concatenated together:
   
173 
   174     def ++
   
175       +
   
176       +
   
177     ;
   
178 
   179 Let's use this new def to see that it adds three numbers
   
180 together:
   
181 
   182     10 10 10 ++ printnum
   
183     30
   
184 
   185 And since we can see that the contents of our new def is,
   
186 indeed, the simple concatenation of its constituents (two
   
187 copies of the 5 byte `+`):
   
188 
   189     inspect ++
   
190     ++: 10 bytes IMMEDIATE COMPILE
   
191         58 5b 1 d8 50 58 5b 1 d8 50
   
192 
   193 The consequence of this is that any def in Meow5 is a
   
194 _complete, stand-alone sequence of instructions_. That's why
   
195 writing an executable for any def is as simple as writing
   
196 its contents to disk! (Plus the ELF header to make it valid,
   
197 of course.)
   
198 
   199 Q: Is this wasteful? Doesn't this cause a lot of redundant
   
200 copies of code?
   
201 
   202 A: Yes.
   
203 
   204 But it's interesting because we're also avoiding a lot of
   
205 branching - most Meow5 code is a continuous stream of
   
206 actions with very tiny relative local jumps. I think it
   
207 produces unusual code.
   
208 
   209 See also:
   
210 
   211 <a href="http://ratfactor.com/assembly-nights2">Assembly Nights 2</a> - This actually part of a series, a personal exploration of the joy of computing.
   
212 
   213 <a href="http://ratfactor.com/meow5/">ratfactor.com/meow5/</a> - Meow5's page on the World Wide Web (though this README is more up to date).
   
214 
   215 
   216 ## Why?
   
217 
   218 My idea came about while studying Forth. A traditional
   
219 "threaded interpreted" Forth goes to some pretty extreme
   
220 lengths to conserve memory. The execution model is not only
   
221 complicated, but seems also likely to not be all that great
   
222 for efficiency on modern machines where memory is much more
   
223 abundant and the bottleneck oftenseems to be getting data
   
224 into the CPU fast enough.
   
225 
   226 In particular, the old Forth literature I have been reading
   
227 is full of statements about needing to conserve the **few
   
228 kilobytes of core memory** on a late 1960s machine.  But
   
229 even my most modest low-powered Celeron and Atom-based
   
230 computers have **L1 CPU caches** that dwarf those
   
231 quantities!
   
232 
   233 So, given the tiny size of the programs I was writing with
   
234 my JONESFORTH port, I kept thinking, "how far could I get if
   
235 I just inlined _everything_?" As in, actually made a copy of
   
236 every word's machine instructions every time it is
   
237 "compiled".
   
238 
   239 I expect this will be silly but fun and educational.