much love
[mu.git] / shell / README.md
bloba8a202136e807bfd47f1353da164d83721126316
1 ### A prototype shell for the Mu computer
3 Currently runs a tiny dialect of Lisp. Steps to run it from the top-level:
5 1. Build it:
7   ```sh
8   ./translate shell/*.mu      # generates code.img
9   ```
11   You can now already run it (under emulation):
12   ```sh
13   qemu-system-i386 code.img
14   ```
16   But let's add some more 'meat' to play with.
18 2. Create a data disk with a library of functions.
20   ```sh
21   dd if=/dev/zero of=data.img count=20160         # approximately 10MB
22   dd if=shell/data.limg of=data.img conv=notrunc
23   ```
25   Equivalently, use `tools/image-data`.
27   ```sh
28   tools/image-data 10 < shell/data.limg
29   ```
31 3. Run with data disk (and 2GB of RAM):
32   ```sh
33   qemu-system-i386 -m 2G -hda code.img -hdb data.img
34   ```
36   <img alt='screenshot of the Mu shell' src='../html/20210624-shell.png'>
38   The Mu computer has a fixed-size screen, which the shell environment
39   partitions into two major regions, with a context-sensitive menu of keyboard
40   shortcuts along the bottom. (No mouse support at the moment.) On the left,
41   two-thirds of the screen is for editing functions and viewing documentation
42   on available primitives. On the right is a REPL where you can try out
43   expressions and see their output. The REPL also has a little toy screen and
44   keyboard for interactively playing with side effects of expressions.
46   Try typing in some expressions at the REPL and hitting `ctrl-s` to see their
47   results. Hit `ctrl-m` to focus on the `...` after a run, and browse how the
48   _trace_ of how the results were computed. [Here's a 2-minute demo](https://archive.org/details/akkartik-mu-2021-05-31).
50   To navigate to a function to edit, hit `ctrl-g`. The list of functions
51   doubles as a bare-bones &ldquo;file system,&rdquo; and functions are
52   rendered in order of most recently touched, like in [Tiddlywiki](https://tiddlywiki.com).
53   There is no scrolling anywhere yet.
55 3. If your Qemu installation supports them, an `-accel` argument
56    will speed up emulation. Try `-accel help` to list your options.
58   Once you select an accelerator, I recommend also adjusting the `responsiveness`
59   mask in shell/evaluate.mu, which controls how frequently the fake screen
60   updates within the REPL. Smaller values will seem more responsive, larger
61   values will run your programs faster. I like to see the fake screen update
62   about once a second. Some suggested values depending on how fast your Qemu
63   is running:
65   - `-accel kvm` on a T420s running Linux: `0xffff/responsiveness=64k`
66   - `-accel tcg` on a 2019 Mac: `0xfff/responsiveness=4k`
68   Putting it all together, here's the command I typically use on Linux:
70   ```
71   qemu-system-i386 -m 2G -accel kvm -hda code.img -hdb data.img
72   ```
74 ### Indent-sensitivity
76 The Mu shell is a Lisp under the hood. However, you'll see a lot fewer
77 parentheses than most Lisps because it can often automatically insert them
78 based on indentation.
80 If you're already used to Lisp and always type in all parens, everything will
81 continue to work. In particular, paren-insertion is disabled inside explicitly
82 added parens. Once Mu sees a `(`, it stops trying to be smart until it sees a
83 `)`.
85 I recommend tastefully only removing parens from top-level (`def`, `mac`,
86 `define`) and control-flow words (`if`, `while`, `for`, etc.) Continue using
87 parens for most real function calls. When in doubt, insert parens.
89 The rule for when parens are inserted is:
91 > Multi-word lines without leading parens are implicitly grouped with later
92 > indented lines
94 For example:
96 ```
97 if (> n 0)      =>      (if (> n 0)
98   34                      34)
99 ```
101 No indented lines after? Parens go around a single line:
104 f a             =>      (f a)
105 f b                     (f b)
108 Lines with a single word are never wrapped in parens:
111 def (foo)       =>      (def (foo)
112   42                      42)
115 Lines with a leading paren never get more parens:
118 def (foo x)     =>      (def (foo x)
119   (print x) x             (print x) x)
122 ### Infix
124 The Mu shell supports infix operators:
126 (3 + 1)
127 => 4
130 You don't need spaces around infix operators:
133 => 4
136 Operator precedence is not hardcoded. Instead, there is just one rule:
137 operators surrounded by whitespace have lower precedence than operators that
138 are not.
140 To see how an expression is parsed, quote it:
142 '3+1
143 => (+ 3 1)
146 You can create your own infix operators:
148 def (a <> b)
149   (not (a = b))
152 To permit arbitrary infix operators, the Mu shell partitions the space of
153 characters (technically Unicode [`code-point`s](../mu.md#variables-registers-and-memory))
154 between operators and regular symbols. As a result, you can't define symbols
155 mixing the two.
157 '*global*
158 => ((* global) . *)                   # probably not what you want
160 'uppercase-char-p
161 => (- (- uppercase char) p)           # probably not what you want
163 '(char> a p)
164 => ((char . >) a p)                   # probably not what you want
167 Infix operators also work in prefix position:
169 (+ 3 1)
170 => 4
173 As a special case, operators can be unary. A silly example:
175 def (+++ x)
176   x+1
178 +++4
179 => 5
182 To pass operators to higher-order functions, wrap them in parens
184 (map1 (+++) '(1 2 3))
185 => (2 3 4)
188 ### Known issues
190 * No mouse support.
192 * Mu currently assumes access to 2GB of RAM. To increase that, modify the
193   definition of `Heap` in 120allocate.subx, and then modify the `-m 2G`
194   argument in the Qemu commands above. Mu currently has no virtual
195   memory. If your Heap is too large for RAM, allocating past the end of RAM
196   will succeed. However, accessing addresses not backed by RAM will fail with
197   this error:
199   ```
200   lookup: failed
201   ```