https://github.com/akkartik/mu/blob/main/apps/browse/main.mu
  1 fn main args-on-stack: (addr array addr array byte) -> _/ebx: int {
  2   var args/eax: (addr array addr array byte) <- copy args-on-stack
  3   var len/ecx: int <- length args
  4   # if (len(args) <= 1) print usage and exit
  5   compare len, 1
  6   {
  7     break-if->
  8     print-string-to-real-screen "usage: browse [filename]\n"
  9     print-string-to-real-screen "    or browse test\n"
 10     return 1
 11   }
 12   # if (args[1] == "test") run-tests()
 13   var tmp/ecx: (addr addr array byte) <- index args, 1
 14   var tmp2/eax: boolean <- string-equal? *tmp, "test"
 15   compare tmp2, 0
 16   {
 17     break-if-=
 18     run-tests
 19     return 0  # TODO: get at Num-test-failures somehow
 20   }
 21   # otherwise interactive mode
 22   var args/eax: (addr array addr array byte) <- copy args-on-stack
 23   var arg/eax: (addr addr array byte) <- index args, 1
 24   var filename/eax: (addr array byte) <- copy *arg
 25   var file-storage: (handle buffered-file)
 26   var file-storage-addr/esi: (addr handle buffered-file) <- address file-storage
 27   open filename, 0, file-storage-addr
 28   var fs/eax: (addr buffered-file) <- lookup file-storage
 29   # if no file, exit
 30   {
 31     compare fs, 0
 32     break-if-!=
 33     print-string-to-real-screen "file not found\n"
 34     return 1
 35   }
 36   #
 37   interactive fs
 38   return 0
 39 }
 40 
 41 fn interactive fs: (addr buffered-file) {
 42   enable-screen-grid-mode
 43   enable-keyboard-immediate-mode
 44   # initialize screen state
 45   var paginated-screen-storage: paginated-screen
 46   var paginated-screen/eax: (addr paginated-screen) <- address paginated-screen-storage
 47   initialize-paginated-screen paginated-screen, 0x40, 2, 5
 48   normal-text paginated-screen
 49   #
 50   {
 51     render paginated-screen, fs
 52     var key/eax: grapheme <- read-key-from-real-keyboard
 53     compare key, 0x71  # 'q'
 54     loop-if-!=
 55   }
 56   enable-keyboard-type-mode
 57   enable-screen-type-mode
 58 }
 59 
 60 fn render screen: (addr paginated-screen), fs: (addr buffered-file) {
 61   start-drawing screen
 62   render-normal screen, fs
 63 }
 64 
 65 fn test-render-multicolumn-text {
 66   # input text
 67   var input-storage: (handle buffered-file)
 68   var input-ah/eax: (addr handle buffered-file) <- address input-storage
 69   populate-buffered-file-containing "abcdefgh", input-ah
 70   var in/eax: (addr buffered-file) <- lookup input-storage
 71   # output screen
 72   var pg: paginated-screen
 73   var pg-addr/ecx: (addr paginated-screen) <- address pg
 74   initialize-fake-paginated-screen pg-addr, 3, 6, 2, 1, 1  # 3 rows, 6 columns, 2 pages * 2 columns each
 75   #
 76   render pg-addr, in
 77   var screen-ah/eax: (addr handle screen) <- get pg, screen
 78   var screen/eax: (addr screen) <- lookup *screen-ah
 79   check-screen-row screen, 1, "      ", "F - test-render-multicolumn-text/row1"
 80   check-screen-row screen, 2, " ab ef", "F - test-render-multicolumn-text/row2"
 81   check-screen-row screen, 3, " cd gh", "F - test-render-multicolumn-text/row3"
 82 }
 83 
 84 fn test-render-heading-text {
 85   # input text
 86   var input-storage: (handle buffered-file)
 87   var input-ah/eax: (addr handle buffered-file) <- address input-storage
 88   populate-buffered-file-containing "# abc\n\ndef", input-ah
 89   var in/eax: (addr buffered-file) <- lookup input-storage
 90   # output screen
 91   var pg: paginated-screen
 92   var pg-addr/ecx: (addr paginated-screen) <- address pg
 93   initialize-fake-paginated-screen pg-addr, 8, 6, 5, 1, 1  # 6 columns, single page
 94   #
 95   render pg-addr, in
 96   var screen-ah/eax: (addr handle screen) <- get pg, screen
 97   var screen/eax: (addr screen) <- lookup *screen-ah
 98   check-screen-row          screen,       1, "      ", "F - test-render-heading-text/row1"
 99   check-screen-row-in-color screen, 0xa0, 2, " abc  ", "F - test-render-heading-text/heading"
100   check-screen-row          screen,       3, "      ", "F - test-render-heading-text/row3"
101   check-screen-row          screen,       4, " def  ", "F - test-render-heading-text/row4"
102 }
103 
104 fn test-render-bold-text {
105   # input text
106   var input-storage: (handle buffered-file)
107   var input-ah/eax: (addr handle buffered-file) <- address input-storage
108   populate-buffered-file-containing "a *b* c", input-ah
109   var in/eax: (addr buffered-file) <- lookup input-storage
110   # output screen
111   var pg: paginated-screen
112   var pg-addr/ecx: (addr paginated-screen) <- address pg
113   initialize-fake-paginated-screen pg-addr, 8, 6, 5, 1, 1  # 6 columns, single page
114   #
115   render pg-addr, in
116   var screen-ah/eax: (addr handle screen) <- get pg, screen
117   var screen/eax: (addr screen) <- lookup *screen-ah
118   check-screen-row         screen, 2, " a b c", "F - test-render-bold-text/text"
119   check-screen-row-in-bold screen, 2, "   b  ", "F - test-render-bold-text/bold"
120 }
121 
122 # terminals don't always support italics, so we'll just always render italics
123 # as bold.
124 fn test-render-pseudoitalic-text {
125   # input text
126   var input-storage: (handle buffered-file)
127   var input-ah/eax: (addr handle buffered-file) <- address input-storage
128   populate-buffered-file-containing "a _b_ c", input-ah
129   var in/eax: (addr buffered-file) <- lookup input-storage
130   # output screen
131   var pg: paginated-screen
132   var pg-addr/ecx: (addr paginated-screen) <- address pg
133   initialize-fake-paginated-screen pg-addr, 8, 6, 5, 1, 1  # 6 columns, single page
134   #
135   render pg-addr, in
136   var screen-ah/eax: (addr handle screen) <- get pg, screen
137   var screen/eax: (addr screen) <- lookup *screen-ah
138   check-screen-row         screen, 2, " a b c", "F - test-render-pseudoitalic-text/text"
139   check-screen-row-in-bold screen, 2, "   b  ", "F - test-render-pseudoitalic-text/bold"
140 }
141 
142 fn test-render-asterisk-in-text {
143   # input text
144   var input-storage: (handle buffered-file)
145   var input-ah/eax: (addr handle buffered-file) <- address input-storage
146   populate-buffered-file-containing "a*b*c", input-ah
147   var in/eax: (addr buffered-file) <- lookup input-storage
148   # output screen
149   var pg: paginated-screen
150   var pg-addr/ecx: (addr paginated-screen) <- address pg
151   initialize-fake-paginated-screen pg-addr, 8, 6, 5, 1, 1  # 6 columns, single page
152   #
153   render pg-addr, in
154   var screen-ah/eax: (addr handle screen) <- get pg, screen
155   var screen/eax: (addr screen) <- lookup *screen-ah
156   check-screen-row         screen, 2, " a*b*c", "F - test-render-bold-text/text"
157   check-screen-row-in-bold screen, 2, "      ", "F - test-render-bold-text/bold"
158 }
159 
160 fn render-normal screen: (addr paginated-screen), fs: (addr buffered-file) {
161   var newline-seen?/esi: boolean <- copy 0  # false
162   var start-of-paragraph?/edi: boolean <- copy 1  # true
163   var previous-grapheme/ebx: grapheme <- copy 0
164 $render-normal:loop: {
165     # if done-drawing?(screen) break
166     var done?/eax: boolean <- done-drawing? screen
167     compare done?, 0  # false
168     break-if-!=
169     var c/eax: grapheme <- read-grapheme-buffered fs
170 $render-normal:loop-body: {
171       # if (c == EOF) break
172       compare c, 0xffffffff  # EOF marker
173       break-if-= $render-normal:loop
174 
175       ## if (c == newline) perform some fairly sophisticated parsing for soft newlines
176       compare c, 0xa  # newline
177       {
178         break-if-!=
179         # if it's the first newline, buffer it
180         compare newline-seen?, 0
181         {
182           break-if-!=
183           newline-seen? <- copy 1  # true
184           break $render-normal:loop-body
185         }
186         # otherwise render two newlines
187         {
188           break-if-=
189           add-grapheme screen, 0xa  # newline
190           add-grapheme screen, 0xa  # newline
191           newline-seen? <- copy 0  # false
192           start-of-paragraph? <- copy 1  # true
193           break $render-normal:loop-body
194         }
195       }
196       # if start of paragraph and c == '#', switch to header
197       compare start-of-paragraph?, 0
198       {
199         break-if-=
200         compare c, 0x23  # '#'
201         {
202           break-if-!=
203           render-header-line screen, fs
204           newline-seen? <- copy 1  # true
205           break $render-normal:loop-body
206         }
207       }
208       # c is not a newline
209       start-of-paragraph? <- copy 0  # false
210       # if c is unprintable (particularly a '\r' CR), skip it
211       compare c, 0x20
212       loop-if-< $render-normal:loop
213       # If there's a newline buffered and c is a space, print the buffered
214       # newline (hard newline).
215       # If there's a newline buffered and c is not a newline or space, print a
216       # space (soft newline).
217       compare newline-seen?, 0  # false
218 $render-normal:flush-buffered-newline: {
219         break-if-=
220         newline-seen? <- copy 0  # false
221         {
222           compare c, 0x20
223           break-if-!=
224           add-grapheme screen, 0xa  # newline
225           break $render-normal:flush-buffered-newline
226         }
227         add-grapheme screen, 0x20  # space
228         # fall through to print c
229       }
230       ## end soft newline support
231 
232 $render-normal:whitespace-separated-regions: {
233         # if previous-grapheme wasn't whitespace, skip this block
234         {
235           compare previous-grapheme, 0x20  # space
236           break-if-=
237           compare previous-grapheme, 0xa  # newline
238           break-if-=
239           break $render-normal:whitespace-separated-regions
240         }
241         # if (c == '*') switch to bold
242         compare c, 0x2a  # '*'
243         {
244           break-if-!=
245           start-color-on-paginated-screen screen, 0xec, 7  # 236 = darkish gray
246           start-bold-on-paginated-screen screen
247             render-until-asterisk screen, fs
248           normal-text screen
249           break $render-normal:loop-body
250         }
251         # if (c == '_') switch to bold
252         compare c, 0x5f  # '_'
253         {
254           break-if-!=
255           start-color-on-paginated-screen screen, 0xec, 7  # 236 = darkish gray
256           start-bold-on-paginated-screen screen
257             render-until-underscore screen, fs
258           normal-text screen
259           break $render-normal:loop-body
260         }
261       }
262       #
263       add-grapheme screen, c
264     }  # $render-normal:loop-body
265     previous-grapheme <- copy c
266     loop
267   }  # $render-normal:loop
268 }
269 
270 fn render-header-line screen: (addr paginated-screen), fs: (addr buffered-file) {
271 $render-header-line:body: {
272   # compute color based on number of '#'s
273   var header-level/esi: int <- copy 1  # caller already grabbed one
274   var c/eax: grapheme <- copy 0
275   {
276     # if done-drawing?(screen) return
277     {
278       var done?/eax: boolean <- done-drawing? screen
279       compare done?, 0  # false
280       break-if-!= $render-header-line:body
281     }
282     #
283     c <- read-grapheme-buffered fs
284     # if (c != '#') break
285     compare c, 0x23  # '#'
286     break-if-!=
287     #
288     header-level <- increment
289     #
290     loop
291   }
292   start-heading screen, header-level
293   {
294     # if done-drawing?(screen) break
295     {
296       var done?/eax: boolean <- done-drawing? screen
297       compare done?, 0  # false
298       break-if-!=
299     }
300     #
301     c <- read-grapheme-buffered fs
302     # if (c == EOF) break
303     compare c, 0xffffffff  # EOF marker
304     break-if-=
305     # if (c == newline) break
306     compare c, 0xa  # newline
307     break-if-=
308     #
309     add-grapheme screen, c
310     #
311     loop
312   }
313   normal-text screen
314 }
315 }
316 
317 # colors for a light background, going from bright to dark (meeting up with bold-text)
318 fn start-heading screen: (addr paginated-screen), header-level: int {
319 $start-heading:body: {
320   start-bold-on-paginated-screen screen
321   compare header-level, 1
322   {
323     break-if-!=
324     start-color-on-paginated-screen screen, 0xa0, 7
325     break $start-heading:body
326   }
327   compare header-level, 2
328   {
329     break-if-!=
330     start-color-on-paginated-screen screen, 0x7c, 7
331     break $start-heading:body
332   }
333   compare header-level, 3
334   {
335     break-if-!=
336     start-color-on-paginated-screen screen, 0x58, 7
337     break $start-heading:body
338   }
339   compare header-level, 4
340   {
341     break-if-!=
342     start-color-on-paginated-screen screen, 0x34, 7
343     break $start-heading:body
344   }
345   start-color-on-paginated-screen screen, 0xe8, 7
346 }
347 }
348 
349 fn render-until-asterisk screen: (addr paginated-screen), fs: (addr buffered-file) {
350   {
351     # if done-drawing?(screen) break
352     var done?/eax: boolean <- done-drawing? screen
353     compare done?, 0  # false
354     break-if-!=
355     #
356     var c/eax: grapheme <- read-grapheme-buffered fs
357     # if (c == EOF) break
358     compare c, 0xffffffff  # EOF marker
359     break-if-=
360     # if (c == '*') break
361     compare c, 0x2a  # '*'
362     break-if-=
363     #
364     add-grapheme screen, c
365     #
366     loop
367   }
368 }
369 
370 fn render-until-underscore screen: (addr paginated-screen), fs: (addr buffered-file) {
371   {
372     # if done-drawing?(screen) break
373     var done?/eax: boolean <- done-drawing? screen
374     compare done?, 0  # false
375     break-if-!=
376     #
377     var c/eax: grapheme <- read-grapheme-buffered fs
378     # if (c == EOF) break
379     compare c, 0xffffffff  # EOF marker
380     break-if-=
381     # if (c == '_') break
382     compare c, 0x5f  # '_'
383     break-if-=
384     #
385     add-grapheme screen, c
386     #
387     loop
388   }
389 }
390 
391 fn normal-text screen: (addr paginated-screen) {
392   reset-formatting-on-paginated-screen screen
393   start-color-on-paginated-screen screen, 0xec, 7  # 236 = darkish gray
394 }