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