https://github.com/akkartik/mu/blob/main/apps/tile/value.mu
  1 fn render-value-at screen: (addr screen), row: int, col: int, _val: (addr value), max-width: int {
  2   move-cursor screen, row, col
  3   var val/esi: (addr value) <- copy _val
  4   var val-type/ecx: (addr int) <- get val, type
  5   # per-type rendering logic goes here
  6   compare *val-type, 1  # string
  7   {
  8     break-if-!=
  9     var val-ah/eax: (addr handle array byte) <- get val, text-data
 10     var val-string/eax: (addr array byte) <- lookup *val-ah
 11     compare val-string, 0
 12     break-if-=
 13     var orig-len/ecx: int <- length val-string
 14     var truncated: (handle array byte)
 15     var truncated-ah/esi: (addr handle array byte) <- address truncated
 16     substring val-string, 0, 0xc, truncated-ah
 17     var truncated-string/eax: (addr array byte) <- lookup *truncated-ah
 18     var len/edx: int <- length truncated-string
 19     start-color screen, 0xf2, 7
 20     print-code-point screen, 0x275d  # open-quote
 21     print-string screen, truncated-string
 22     compare len, orig-len
 23     {
 24       break-if-=
 25       print-code-point screen, 0x2026  # ellipses
 26     }
 27     print-code-point screen, 0x275e  # close-quote
 28     reset-formatting screen
 29     return
 30   }
 31   compare *val-type, 2  # array
 32   {
 33     break-if-!=
 34     var val-ah/eax: (addr handle array value) <- get val, array-data
 35     var val-array/eax: (addr array value) <- lookup *val-ah
 36     render-array-at screen, row, col, val-array
 37     return
 38   }
 39   compare *val-type, 3  # file
 40   {
 41     break-if-!=
 42     var val-ah/eax: (addr handle buffered-file) <- get val, file-data
 43     var val-file/eax: (addr buffered-file) <- lookup *val-ah
 44     start-color screen, 0, 7
 45     # TODO
 46     print-string screen, " FILE "
 47     return
 48   }
 49   compare *val-type, 4  # screen
 50   {
 51     break-if-!=
 52 #?     print-string 0, "render-screen"
 53     var val-ah/eax: (addr handle screen) <- get val, screen-data
 54     var val-screen/eax: (addr screen) <- lookup *val-ah
 55     render-screen screen, row, col, val-screen
 56 #?     print-string 0, "}\n"
 57     return
 58   }
 59   # render ints by default for now
 60   var val-num/eax: (addr float) <- get val, number-data
 61   render-number screen, *val-num, max-width
 62 }
 63 
 64 # synaesthesia
 65 # TODO: right-justify
 66 fn render-number screen: (addr screen), val: float, max-width: int {
 67   # if max-width is 0, we're inside an array. No coloring.
 68   compare max-width, 0
 69   {
 70     break-if-!=
 71     print-float-decimal-approximate screen, val, 3
 72     return
 73   }
 74   var val-int/eax: int <- convert val
 75   var bg/eax: int <- hash-color val-int
 76   var fg/ecx: int <- copy 7
 77   {
 78     compare bg, 2
 79     break-if-!=
 80     fg <- copy 0
 81   }
 82   {
 83     compare bg, 3
 84     break-if-!=
 85     fg <- copy 0
 86   }
 87   {
 88     compare bg, 6
 89     break-if-!=
 90     fg <- copy 0
 91   }
 92   start-color screen, fg, bg
 93   print-grapheme screen, 0x20  # space
 94   print-float-decimal-approximate screen, val, 3
 95   print-grapheme screen, 0x20  # space
 96 }
 97 
 98 fn render-array-at screen: (addr screen), row: int, col: int, _a: (addr array value) {
 99   start-color screen, 0xf2, 7
100   # don't surround in spaces
101   print-grapheme screen, 0x5b  # '['
102   increment col
103   var a/esi: (addr array value) <- copy _a
104   var max/ecx: int <- length a
105   var i/eax: int <- copy 0
106   {
107     compare i, max
108     break-if->=
109     {
110       compare i, 0
111       break-if-=
112       print-string screen, " "
113     }
114     var off/ecx: (offset value) <- compute-offset a, i
115     var x/ecx: (addr value) <- index a, off
116     render-value-at screen, row, col, x, 0
117     {
118       var w/eax: int <- value-width x, 0
119       add-to col, w
120       increment col
121     }
122     i <- increment
123     loop
124   }
125   print-grapheme screen, 0x5d  # ']'
126 }
127 
128 fn render-screen screen: (addr screen), row: int, col: int, _target-screen: (addr screen) {
129   reset-formatting screen
130   move-cursor screen, row, col
131   var target-screen/esi: (addr screen) <- copy _target-screen
132   var ncols-a/ecx: (addr int) <- get target-screen, num-cols
133   print-upper-border screen, *ncols-a
134   var r/edx: int <- copy 1
135   var nrows-a/ebx: (addr int) <- get target-screen, num-rows
136   {
137     compare r, *nrows-a
138     break-if->
139     increment row  # mutate arg
140     move-cursor screen, row, col
141     print-string screen, " "
142     var c/edi: int <- copy 1
143     {
144       compare c, *ncols-a
145       break-if->
146       print-screen-cell-of-fake-screen screen, target-screen, r, c
147       c <- increment
148       loop
149     }
150     print-string screen, " "
151     r <- increment
152     loop
153   }
154   increment row  # mutate arg
155   move-cursor screen, row, col
156   print-lower-border screen, *ncols-a
157 }
158 
159 fn hash-color val: int -> _/eax: int {
160   var quotient/eax: int <- copy 0
161   var remainder/edx: int <- copy 0
162   quotient, remainder <- integer-divide val, 7  # assumes that 7 is always the background color
163   return remainder
164 }
165 
166 fn print-screen-cell-of-fake-screen screen: (addr screen), _target: (addr screen), _row: int, _col: int {
167   start-color screen, 0, 0xf6
168   var target/esi: (addr screen) <- copy _target
169   var row/ecx: int <- copy _row
170   var col/edx: int <- copy _col
171   # if cursor is at screen-cell, add some fancy
172   {
173     var cursor-row/eax: (addr int) <- get target, cursor-row
174     compare *cursor-row, row
175     break-if-!=
176     var cursor-col/eax: (addr int) <- get target, cursor-col
177     compare *cursor-col, col
178     break-if-!=
179     start-blinking screen
180     start-color screen, 0, 1
181   }
182   var g/eax: grapheme <- screen-grapheme-at target, row, col
183   {
184     compare g, 0
185     break-if-!=
186     g <- copy 0x20  # space
187   }
188   print-grapheme screen, g
189   reset-formatting screen
190 }
191 
192 fn print-upper-border screen: (addr screen), width: int {
193   print-code-point screen, 0x250c  # top-left corner
194   var i/eax: int <- copy 0
195   {
196     compare i, width
197     break-if->=
198     print-code-point screen, 0x2500  # horizontal line
199     i <- increment
200     loop
201   }
202   print-code-point screen, 0x2510  # top-right corner
203 }
204 
205 fn print-lower-border screen: (addr screen), width: int {
206   print-code-point screen, 0x2514  # bottom-left corner
207   var i/eax: int <- copy 0
208   {
209     compare i, width
210     break-if->=
211     print-code-point screen, 0x2500  # horizontal line
212     i <- increment
213     loop
214   }
215   print-code-point screen, 0x2518  # bottom-right corner
216 }
217 
218 fn value-width _v: (addr value), top-level: boolean -> _/eax: int {
219   var v/esi: (addr value) <- copy _v
220   var type/eax: (addr int) <- get v, type
221   {
222     compare *type, 0  # int
223     break-if-!=
224     var v-num/edx: (addr float) <- get v, number-data
225     var result/eax: int <- float-size *v-num, 3
226     return result
227   }
228   {
229     compare *type, 1  # string
230     break-if-!=
231     var s-ah/eax: (addr handle array byte) <- get v, text-data
232     var s/eax: (addr array byte) <- lookup *s-ah
233     compare s, 0
234     break-if-=
235     var result/eax: int <- length s
236     compare result, 0xd  # max string size
237     {
238       break-if-<=
239       result <- copy 0xd
240     }
241     # if it's a nested string, include space for quotes
242     # we don't do this for the top-level, where the quotes will overflow
243     # into surrounding padding.
244     compare top-level, 0  # false
245     {
246       break-if-!=
247       result <- add 2
248     }
249     return result
250   }
251   {
252     compare *type, 2  # array
253     break-if-!=
254     var a-ah/eax: (addr handle array value) <- get v, array-data
255     var a/eax: (addr array value) <- lookup *a-ah
256     compare a, 0
257     break-if-=
258     var result/eax: int <- array-width a
259     return result
260   }
261   {
262     compare *type, 3  # file handle
263     break-if-!=
264     var f-ah/eax: (addr handle buffered-file) <- get v, file-data
265     var f/eax: (addr buffered-file) <- lookup *f-ah
266     compare f, 0
267     break-if-=
268     # TODO: visualizing file handles
269     return 4
270   }
271   {
272     compare *type, 4  # screen
273     break-if-!=
274     var screen-ah/eax: (addr handle screen) <- get v, screen-data
275     var screen/eax: (addr screen) <- lookup *screen-ah
276     compare screen, 0
277     break-if-=
278     var ncols/ecx: (addr int) <- get screen, num-cols
279     var result/eax: int <- copy *ncols
280     result <- add 2  # left/right margins
281     return *ncols
282   }
283   return 0
284 }
285 
286 # keep sync'd with render-array-at
287 fn array-width _a: (addr array value) -> _/eax: int {
288   var a/esi: (addr array value) <- copy _a
289   var max/ecx: int <- length a
290   var i/eax: int <- copy 0
291   var result/edi: int <- copy 0
292   {
293     compare i, max
294     break-if->=
295     {
296       compare i, 0
297       break-if-=
298       result <- increment  # for space
299     }
300     var off/ecx: (offset value) <- compute-offset a, i
301     var x/ecx: (addr value) <- index a, off
302     {
303       var w/eax: int <- value-width x, 0
304       result <- add w
305     }
306     i <- increment
307     loop
308   }
309   # we won't add 2 for surrounding brackets since we don't surround arrays in
310   # spaces like other value types
311   return result
312 }
313 
314 fn value-height _v: (addr value) -> _/eax: int {
315   var v/esi: (addr value) <- copy _v
316   var type/eax: (addr int) <- get v, type
317   {
318     compare *type, 3  # file handle
319     break-if-!=
320     # TODO: visualizing file handles
321     return 1
322   }
323   {
324     compare *type, 4  # screen
325     break-if-!=
326     var screen-ah/eax: (addr handle screen) <- get v, screen-data
327     var screen/eax: (addr screen) <- lookup *screen-ah
328     compare screen, 0
329     break-if-=
330     var nrows/ecx: (addr int) <- get screen, num-rows
331     var result/eax: int <- copy *nrows
332     result <- add 2  # top and bottom border
333     return result
334   }
335   return 1
336 }
337 
338 fn deep-copy-value _src: (addr value), _dest: (addr value) {
339 #?   print-string 0, "deep-copy-value\n"
340   var src/esi: (addr value) <- copy _src
341   var dest/edi: (addr value) <- copy _dest
342   var type/ebx: (addr int) <- get src, type
343   var y/ecx: (addr int) <- get dest, type
344   copy-object type, y
345   compare *type, 0   # int
346   {
347     break-if-!=
348 #?     print-string 0, "int value\n"
349     var src-n/eax: (addr float) <- get src, number-data
350     var dest-n/ecx: (addr float) <- get dest, number-data
351     copy-object src-n, dest-n
352     return
353   }
354   compare *type, 1  # string
355   {
356     break-if-!=
357 #?     print-string 0, "string value\n"
358     var src-ah/eax: (addr handle array byte) <- get src, text-data
359     var src/eax: (addr array byte) <- lookup *src-ah
360     var dest-ah/edx: (addr handle array byte) <- get dest, text-data
361     copy-array-object src, dest-ah
362     return
363   }
364   compare *type, 2  # array
365   {
366     break-if-!=
367 #?     print-string 0, "array value\n"
368     var src-ah/eax: (addr handle array value) <- get src, array-data
369     var _src/eax: (addr array value) <- lookup *src-ah
370     var src/esi: (addr array value) <- copy _src
371     var n/ecx: int <- length src
372     var dest-ah/edx: (addr handle array value) <- get dest, array-data
373     populate dest-ah, n
374     var _dest/eax: (addr array value) <- lookup *dest-ah
375     var dest/edi: (addr array value) <- copy _dest
376     var i/eax: int <- copy 0
377     {
378       compare i, n
379       break-if->=
380       {
381         var offset/edx: (offset value) <- compute-offset src, i
382         var src-element/eax: (addr value) <- index src, offset
383         var dest-element/ecx: (addr value) <- index dest, offset
384         deep-copy-value src-element, dest-element
385       }
386       i <- increment
387       loop
388     }
389     copy-array-object src, dest-ah
390     return
391   }
392   compare *type, 3  # file
393   {
394     break-if-!=
395 #?     print-string 0, "file value\n"
396     var src-filename-ah/eax: (addr handle array byte) <- get src, filename
397     var _src-filename/eax: (addr array byte) <- lookup *src-filename-ah
398     var src-filename/ecx: (addr array byte) <- copy _src-filename
399     var dest-filename-ah/ebx: (addr handle array byte) <- get dest, filename
400     copy-array-object src-filename, dest-filename-ah
401     var src-file-ah/eax: (addr handle buffered-file) <- get src, file-data
402     var src-file/eax: (addr buffered-file) <- lookup *src-file-ah
403     var dest-file-ah/edx: (addr handle buffered-file) <- get dest, file-data
404     copy-file src-file, dest-file-ah, src-filename
405     return
406   }
407   compare *type, 4  # screen
408   {
409     break-if-!=
410 #?     print-string 0, "screen value\n"
411     var src-screen-ah/eax: (addr handle screen) <- get src, screen-data
412     var _src-screen/eax: (addr screen) <- lookup *src-screen-ah
413     var src-screen/ecx: (addr screen) <- copy _src-screen
414     var dest-screen-ah/eax: (addr handle screen) <- get dest, screen-data
415     allocate dest-screen-ah
416     var dest-screen/eax: (addr screen) <- lookup *dest-screen-ah
417     copy-object src-screen, dest-screen
418     var dest-screen-data-ah/ebx: (addr handle array screen-cell) <- get dest-screen, data
419     var src-screen-data-ah/eax: (addr handle array screen-cell) <- get src-screen, data
420     var src-screen-data/eax: (addr array screen-cell) <- lookup *src-screen-data-ah
421     copy-array-object src-screen-data, dest-screen-data-ah
422     return
423   }
424 }