https://github.com/akkartik/mu/blob/main/browse-slack/main.mu
1 type channel {
2 name: (handle array byte)
3 posts: (handle array int)
4 posts-first-free: int
5 }
6
7 type user {
8 id: (handle array byte)
9 name: (handle array byte)
10 real-name: (handle array byte)
11 avatar: (handle image)
12 }
13
14 type item {
15 id: (handle array byte)
16 channel: (handle array byte)
17 by: int
18 text: (handle array byte)
19 parent: int
20 comments: (handle array int)
21 comments-first-free: int
22 }
23
24 type item-list {
25 data: (handle array item)
26 data-first-free: int
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 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
52
53 var s-h: (handle stream byte)
54 var s-ah/eax: (addr handle stream byte) <- address s-h
55 populate-stream s-ah, 0x4000000/data-size
56 var _s/eax: (addr stream byte) <- lookup *s-ah
57 var s/ebx: (addr stream byte) <- copy _s
58 var sector-count/eax: int <- copy 0x400
59
60 set-cursor-position 0/screen, 0x20/x 0/y
61 draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "loading ", 3/fg 0/bg
62 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, sector-count, 3/fg 0/bg
63 draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " sectors from data disk..", 3/fg 0/bg
64 load-sectors data-disk, 0/lba, sector-count, s
65 draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "done", 3/fg 0/bg
66
67 var users-h: (handle array user)
68 var users-ah/eax: (addr handle array user) <- address users-h
69 populate users-ah, 0x800/num-users
70 var _users/eax: (addr array user) <- lookup *users-ah
71 var users/edi: (addr array user) <- copy _users
72 var channels-h: (handle array channel)
73 var channels-ah/eax: (addr handle array channel) <- address channels-h
74 populate channels-ah, 0x20/num-channels
75 var _channels/eax: (addr array channel) <- lookup *channels-ah
76 var channels/esi: (addr array channel) <- copy _channels
77 var items-storage: item-list
78 var items/edx: (addr item-list) <- address items-storage
79 var items-data-ah/eax: (addr handle array item) <- get items, data
80 populate items-data-ah, 0x10000/num-items
81 parse s, users, channels, items
82
83 var env-storage: environment
84 var env/ebx: (addr environment) <- address env-storage
85 initialize-environment env, items
86 {
87 render-environment screen, env, users, channels, items
88 {
89 var key/eax: byte <- read-key keyboard
90 compare key, 0
91 loop-if-=
92 update-environment env, key, users, channels, items
93 }
94 loop
95 }
96 }
97
98 fn parse in: (addr stream byte), users: (addr array user), channels: (addr array channel), _items: (addr item-list) {
99 var items/esi: (addr item-list) <- copy _items
100 var items-data-ah/eax: (addr handle array item) <- get items, data
101 var _items-data/eax: (addr array item) <- lookup *items-data-ah
102 var items-data/edi: (addr array item) <- copy _items-data
103
104 var record-storage: (stream byte 0x18000)
105 var record/ecx: (addr stream byte) <- address record-storage
106 var user-idx/edx: int <- copy 0
107 var item-idx/ebx: int <- copy 0
108 {
109 var done?/eax: boolean <- stream-empty? in
110 compare done?, 0/false
111 break-if-!=
112 var c/eax: byte <- peek-byte in
113 compare c, 0
114 break-if-=
115 set-cursor-position 0/screen, 0x20/x 1/y
116 draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "parsed " 3/fg 0/bg
117 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, user-idx, 3/fg 0/bg
118 draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " users, " 3/fg 0/bg
119 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, item-idx, 3/fg 0/bg
120 draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " posts/comments" 3/fg 0/bg
121 clear-stream record
122 parse-record in, record
123 var user?/eax: boolean <- user-record? record
124 {
125 compare user?, 0/false
126 break-if-=
127 parse-user record, users, user-idx
128 user-idx <- increment
129 }
130 {
131 compare user?, 0/false
132 break-if-!=
133 parse-item record, channels, items-data, item-idx
134 item-idx <- increment
135 }
136 loop
137 }
138 var dest/eax: (addr int) <- get items, data-first-free
139 copy-to *dest, item-idx
140 }
141
142 fn parse-record in: (addr stream byte), out: (addr stream byte) {
143 var paren/eax: byte <- read-byte in
144 compare paren, 0x28/open-paren
145 {
146 break-if-=
147 set-cursor-position 0/screen, 0x20 0x10
148 var c/eax: int <- copy paren
149 draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen c, 5/fg 0/bg
150 abort "parse-record: ("
151 }
152 var paren-int/eax: int <- copy paren
153 append-byte out, paren-int
154 {
155 {
156 var eof?/eax: boolean <- stream-empty? in
157 compare eof?, 0/false
158 break-if-=
159 abort "parse-record: truncated; increase the sector-count to load from disk"
160 }
161 var c/eax: byte <- read-byte in
162 {
163 var c-int/eax: int <- copy c
164 append-byte out, c-int
165 }
166 compare c, 0x29/close-paren
167 break-if-=
168 compare c, 0x22/double-quote
169 {
170 break-if-!=
171 slurp-json-string in, out
172 }
173 loop
174 }
175 skip-chars-matching-whitespace in
176 }
177
178 fn user-record? record: (addr stream byte) -> _/eax: boolean {
179 rewind-stream record
180 var c/eax: byte <- read-byte record
181 var c/eax: byte <- read-byte record
182 var c/eax: byte <- read-byte record
183 compare c, 0x55/U
184 {
185 break-if-!=
186 return 1/true
187 }
188 rewind-stream record
189 return 0/false
190 }
191
192 fn parse-user record: (addr stream byte), _users: (addr array user), user-idx: int {
193 var users/esi: (addr array user) <- copy _users
194 var offset/eax: (offset user) <- compute-offset users, user-idx
195 var user/esi: (addr user) <- index users, offset
196
197 var s-storage: (stream byte 0x100)
198 var s/ecx: (addr stream byte) <- address s-storage
199
200 rewind-stream record
201 var paren/eax: byte <- read-byte record
202 compare paren, 0x28/open-paren
203 {
204 break-if-=
205 abort "parse-user: ("
206 }
207
208 skip-chars-matching-whitespace record
209 var double-quote/eax: byte <- read-byte record
210 compare double-quote, 0x22/double-quote
211 {
212 break-if-=
213 abort "parse-user: id"
214 }
215 next-json-string record, s
216 var dest/eax: (addr handle array byte) <- get user, id
217 stream-to-array s, dest
218
219 skip-chars-matching-whitespace record
220 var double-quote/eax: byte <- read-byte record
221 compare double-quote, 0x22/double-quote
222 {
223 break-if-=
224 abort "parse-user: name"
225 }
226 clear-stream s
227 next-json-string record, s
228 var dest/eax: (addr handle array byte) <- get user, name
229 stream-to-array s, dest
230
231 skip-chars-matching-whitespace record
232 var double-quote/eax: byte <- read-byte record
233 compare double-quote, 0x22/double-quote
234 {
235 break-if-=
236 abort "parse-user: real-name"
237 }
238 clear-stream s
239 next-json-string record, s
240 var dest/eax: (addr handle array byte) <- get user, real-name
241 stream-to-array s, dest
242
243 skip-chars-matching-whitespace record
244 var open-bracket/eax: byte <- read-byte record
245 compare open-bracket, 0x5b/open-bracket
246 {
247 break-if-=
248 abort "parse-user: avatar"
249 }
250 skip-chars-matching-whitespace record
251 var c/eax: byte <- peek-byte record
252 {
253 compare c, 0x5d/close-bracket
254 break-if-=
255 var dest-ah/eax: (addr handle image) <- get user, avatar
256 allocate dest-ah
257 var dest/eax: (addr image) <- lookup *dest-ah
258 initialize-image dest, record
259 }
260 }
261
262 fn parse-item record: (addr stream byte), _channels: (addr array channel), _items: (addr array item), item-idx: int {
263 var items/esi: (addr array item) <- copy _items
264 var offset/eax: (offset item) <- compute-offset items, item-idx
265 var item/edi: (addr item) <- index items, offset
266
267 var s-storage: (stream byte 0x40)
268 var s/ecx: (addr stream byte) <- address s-storage
269
270 rewind-stream record
271 var paren/eax: byte <- read-byte record
272 compare paren, 0x28/open-paren
273 {
274 break-if-=
275 abort "parse-item: ("
276 }
277
278 skip-chars-matching-whitespace record
279 var double-quote/eax: byte <- read-byte record
280 compare double-quote, 0x22/double-quote
281 {
282 break-if-=
283 abort "parse-item: id"
284 }
285 next-json-string record, s
286 var dest/eax: (addr handle array byte) <- get item, id
287 stream-to-array s, dest
288
289 {
290 var word-slice-storage: slice
291 var word-slice/ecx: (addr slice) <- address word-slice-storage
292 next-word record, word-slice
293 var src/eax: int <- parse-decimal-int-from-slice word-slice
294 compare src, -1
295 break-if-=
296 var dest/edx: (addr int) <- get item, parent
297 copy-to *dest, src
298
299 var parent-offset/eax: (offset item) <- compute-offset items, src
300 var parent-item/esi: (addr item) <- index items, parent-offset
301 var parent-comments-ah/ebx: (addr handle array int) <- get parent-item, comments
302 var parent-comments/eax: (addr array int) <- lookup *parent-comments-ah
303 compare parent-comments, 0
304 {
305 break-if-!=
306 populate parent-comments-ah, 0x200/num-comments
307 parent-comments <- lookup *parent-comments-ah
308 }
309 var parent-comments-first-free-addr/edi: (addr int) <- get parent-item, comments-first-free
310 var parent-comments-first-free/edx: int <- copy *parent-comments-first-free-addr
311 var dest/eax: (addr int) <- index parent-comments, parent-comments-first-free
312 var src/ecx: int <- copy item-idx
313 copy-to *dest, src
314 increment *parent-comments-first-free-addr
315 }
316
317 skip-chars-matching-whitespace record
318 var double-quote/eax: byte <- read-byte record
319 compare double-quote, 0x22/double-quote
320 {
321 break-if-=
322 abort "parse-item: channel"
323 }
324 clear-stream s
325 next-json-string record, s
326 var dest/eax: (addr handle array byte) <- get item, channel
327 stream-to-array s, dest
328
329
330 {
331 var channels/esi: (addr array channel) <- copy _channels
332 var channel-index/eax: int <- find-or-insert channels, s
333
334 var channel-offset/eax: (offset channel) <- compute-offset channels, channel-index
335 var channel/eax: (addr channel) <- index channels, channel-offset
336
337
338
339
340 var channel-posts-ah/ecx: (addr handle array int) <- get channel, posts
341 var channel-posts-first-free-addr/edx: (addr int) <- get channel, posts-first-free
342
343
344
345
346 var channel-posts-first-free/ebx: int <- copy *channel-posts-first-free-addr
347
348 var channel-posts/eax: (addr array int) <- lookup *channel-posts-ah
349 var dest/eax: (addr int) <- index channel-posts, channel-posts-first-free
350 var src/ecx: int <- copy item-idx
351 copy-to *dest, src
352 increment *channel-posts-first-free-addr
353 }
354
355 {
356 var word-slice-storage: slice
357 var word-slice/ecx: (addr slice) <- address word-slice-storage
358 next-word record, word-slice
359 var src/eax: int <- parse-decimal-int-from-slice word-slice
360 var dest/edx: (addr int) <- get item, by
361 copy-to *dest, src
362 }
363
364 var s-storage: (stream byte 0x4000)
365 var s/ecx: (addr stream byte) <- address s-storage
366 skip-chars-matching-whitespace record
367 var double-quote/eax: byte <- read-byte record
368 compare double-quote, 0x22/double-quote
369 {
370 break-if-=
371 abort "parse-item: text"
372 }
373 next-json-string record, s
374 var dest/eax: (addr handle array byte) <- get item, text
375 stream-to-array s, dest
376 }
377
378 fn find-or-insert _channels: (addr array channel), name: (addr stream byte) -> _/eax: int {
379 var channels/esi: (addr array channel) <- copy _channels
380 var i/ecx: int <- copy 0
381 var max/edx: int <- length channels
382 {
383 compare i, max
384 break-if->=
385 var offset/eax: (offset channel) <- compute-offset channels, i
386 var curr/ebx: (addr channel) <- index channels, offset
387 var curr-name-ah/edi: (addr handle array byte) <- get curr, name
388 var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
389 {
390 compare curr-name, 0
391 break-if-!=
392 rewind-stream name
393 stream-to-array name, curr-name-ah
394 var posts-ah/eax: (addr handle array int) <- get curr, posts
395 populate posts-ah, 0x8000/channel-capacity
396 return i
397 }
398 var found?/eax: boolean <- stream-data-equal? name, curr-name
399 {
400 compare found?, 0/false
401 break-if-=
402 return i
403 }
404 i <- increment
405 loop
406 }
407 abort "out of channels"
408 return -1
409 }
410
411
412 fn slurp-json-string in: (addr stream byte), out: (addr stream byte) {
413
414 {
415 {
416 var eof?/eax: boolean <- stream-empty? in
417 compare eof?, 0/false
418 break-if-=
419 abort "slurp-json-string: truncated"
420 }
421 var c/eax: byte <- read-byte in
422 {
423 var c-int/eax: int <- copy c
424 append-byte out, c-int
425 }
426 compare c, 0x22/double-quote
427 break-if-=
428 compare c, 0x5c/backslash
429 {
430 break-if-!=
431
432 c <- read-byte in
433 var c-int/eax: int <- copy c
434 append-byte out, c-int
435 }
436 loop
437 }
438 }
439
440
441 fn next-json-string in: (addr stream byte), out: (addr stream byte) {
442
443 {
444 {
445 var eof?/eax: boolean <- stream-empty? in
446 compare eof?, 0/false
447 break-if-=
448 abort "next-json-string: truncated"
449 }
450 var c/eax: byte <- read-byte in
451 compare c, 0x22/double-quote
452 break-if-=
453 {
454 var c-int/eax: int <- copy c
455 append-byte out, c-int
456 }
457 compare c, 0x5c/backslash
458 {
459 break-if-!=
460
461 c <- read-byte in
462 var c-int/eax: int <- copy c
463 append-byte out, c-int
464 }
465 loop
466 }
467 }