1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
# onboarding
a reading and verification order for getting from a fresh checkout to working
knowledge of tinyvm and zc. each step names what to read, what to run, and what
you should be able to answer afterwards. the steps build on each other, so follow
them in order; the whole pass takes a few hours, most of it reading.
the order was recorded while learning the code from scratch, starting with the
markdown files (the fastest way in) and only then the sources.
## 0. the shape of the repository
read `README.md` from the top through "build and run", then skim its section
headings. list the tree:
```sh
ls; ls src src/*; ls tests docs docs/design
```
- the root holds entry points only: `Makefile`, `tinyvm.sh`, `tinyvm.bat`, the
markdown files.
- `src/` holds every source: `core/` (the c vm), `micro/` (the portable vm),
`runtime/` and `compiler/` (zc sources that become the genome), `lib/` (std,
icon, actor), `lsp/` (the language server, in zc), `tools/` (bootstrap and build
tools), `js/`, `web/`, `baremetal/`.
- `build/` holds everything generated and is not in git. nothing outside it is
ever written by a build or a test.
you should be able to say: what the genome is (the compiler and runtime, compiled
to vm words, embedded in every core), and which files make it
(`src/runtime/rt.zc`, `src/core/core.zc`, `src/compiler/compiler.zc`).
## 1. build it from nothing
the cores embed their genome with c23 `#embed`, so you need clang 19 or gcc 15
or later; `sh src/tools/findcc.sh` prints the compiler `make` will use.
```sh
make clean
make
build/bin/zy < examples/hello.zc
```
watch the output of `make`: the default memory map is generated
(`build/core/map/`), zc0 compiles the genome sources (stage 0), a core built
around that image compiles the compiler again (stage 1) and again (stage 2), the
two self-compiled images are compared, and the first becomes
`build/core/genome.bin`. then `zy`, `zym` and `zclsp` are built around it.
you should be able to say: why the bootstrap needs three stages, what `mkgenome`
protects against (a compiler's error report taken for an image), and how a core
finds its image (`src/core/genome.h`, `--embed-dir`).
## 2. the language, by example
read `docs/LEARN.md` while running its examples:
```sh
for f in examples/learn/*.zc; do echo "== $f"; build/bin/zy < $f; done
python3 tests/drivers/learn.py build/bin/zy
```
then read the language parts of `README.md`: "zc, the language", "memory model",
"types", "o3", "code as data". run a few of the larger examples
(`examples/lang.zc`, `examples/bf.zc`, `examples/dump.zc`, `examples/tree.zc`).
you should be able to write a small program that includes `rt`, allocates with
`rt.new(T)`, passes an owner to a `.d` parameter, and returns one with `.u`; and
explain why `include rt;` is required before `rt.put_n`.
## 3. ownership, the analyzer's view
read `docs/BORROW.md`, then `docs/history/BOOTSTRAP-OWNERSHIP.md` (how two missing rules
once stopped the compiler compiling itself). run the rejection suites and read a
few of the fixtures, each of which starts with the message it expects:
```sh
bash tests/runerr.sh build/bin/zy tests/errors
bash tests/runerr.sh build/bin/zy tests/typeerrors
bash tests/runerr.sh build/bin/zy tests/langerrors
```
in `src/compiler/compiler.zc`, find the block that starts at
`// the rules below are the heart of the analyzer` and read `recast`,
`argcheck`, `assigncheck` and `retcheck`, then `exit_vars` and the `lend_*`
helpers.
you should be able to say what states a local can be in (plain, m, u, d, child,
borrow, dead) and what each of `rt.free(r); r = replacement;` and `g = m*x;`
does to them.
## 4. the compiler's structure
`src/compiler/compiler.zc` is about ten thousand lines and begins with an
overview. read the overview, then use the section markers to navigate:
```sh
grep -n "^// ----" src/compiler/compiler.zc
```
read in this order: interning and symbols, the lexer (`lex1`), expressions
(`primary`, `unary`, `bin`, `expr`), statements and keywords (`add_kw` and the
`kw_*` functions), functions (`fndef`, `fn_begin`, `prologue`), globals
(`gdecl2`, `kw_embed`), then `chk_unit` (check mode for the language server) and
`build_image` (image mode for `src/tools/build.zc`).
you should be able to say where a new keyword is registered, how a keyword
works at the top level and inside a function, and how the compiler emits a
byte-string literal into the code stream.
## 5. the vm cores
read "the instruction set", "memory map and abi", "how the core works" and "the
microcore" in `README.md`, then `docs/design/memory.md`. in `src/core/core.c`,
find `main` and follow it to `run(GORG)`; in `src/micro/micro.c`, `micro_run`.
```sh
build/bin/genmap --preset default --check | head
ZYTRACE=1 build/bin/zy < tests/programs/o3.zc 2>&1 | head -20
```
you should be able to say where the genome is loaded, where the syscall gate is,
and which backends (interpreter, arm64 jit, llvm, microcore, javascript, wasm)
run the same image.
## 6. the toolchain around it
read "language server", "vscode extension", "javascript & typescript runtime"
and "web" in `README.md`, then `editors/vscode/README.md`. build the rest:
```sh
make js web lspimage
node build/js/tinyvm.js examples/hello.zc
build/bin/zclsp < examples/opt_test.zc
```
skim `src/lsp/server.zc` from its overview: the check (`check_doc`), hovers
(`do_hover`), navigation, the formatter (`fm_*`).
you should be able to say how the editor gets diagnostics (the real compiler in
check mode), and why the javascript and wasm servers need no native build.
## 7. the libraries
read `docs/ICON.md` and `docs/ACTOR.md`, then the headers of `src/lib/std.zc`,
`src/lib/icon.zc` and `src/lib/actor.zc`. run their fixtures:
```sh
python3 tests/drivers/icon_test.py
python3 tests/drivers/actor_test.py build/bin/zy
```
you should be able to say how `include actor;` adds the `actor` keyword without
any actor code in the compiler (`run.syntax`, `run.statement_hook`).
## 8. the tests
read the header of `tests/test.sh` and `docs/design/test-coverage.md`. run the
quick suite, then the whole one when there is time:
```sh
make check
make test JOBS=4
```
`make parity` compiles the programs in `tests/programs/` and `examples/` with zc0 and
with the self-hosted compiler and diffs their output; `tests/programs/parity_ops.zc`
pins operator precedence. it uses `build/`, and skips what zc0 rejects.
the suite builds its own map, bootstrap and cores in a scratch directory, so it
does not depend on what is in `build/`. on a host without the arm64 jit the
language server is interpreted and slow; `ZC_TEST_TIMEOUT_SCALE` stretches the
drivers' deadlines (the suite sets it). `LLVM_CONFIG` selects an llvm with
headers for the llvm section.
## 9. history and open work
`docs/history/README.md` is the scrapbook of earlier stages and `TODO.md` the
maintenance checklist. read the
open items in `TODO.md` last: by then each one should point at code you have
seen.
## checklist
after the pass you should be able to do each of these without looking anything up:
- rebuild from `make clean`, and say which file each step writes under `build/`
- add a keyword to the compiler and rebuild the genome
- write a program that the analyzer rejects, and predict the message
- build a run-only image (`make run PROG=...`) and say why its source must fit zc0
- find a built-in's declaration from a hover in the editor or with the server
|