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