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