https://github.com/akkartik/mu/blob/main/shell/grapheme-stack.mu

      1 # grapheme stacks are the smallest unit of editable text
      2
      3 type grapheme-stack {
      4   data: (handle array grapheme)
      5   top: int
      6 }
      7
      8 fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int {
      9   var self/esi: (addr grapheme-stack) <- copy _self
     10   var d/edi: (addr handle array grapheme) <- get self, data
     11   populate d, n
     12   var top/eax: (addr int) <- get self, top
     13   copy-to *top, 0
     14 }
     15
     16 fn clear-grapheme-stack _self: (addr grapheme-stack) {
     17   var self/esi: (addr grapheme-stack) <- copy _self
     18   var top/eax: (addr int) <- get self, top
     19   copy-to *top, 0
     20 }
     21
     22 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean {
     23   var self/esi: (addr grapheme-stack) <- copy _self
     24   var top/eax: (addr int) <- get self, top
     25   compare *top, 0
     26   {
     27     break-if-!=
     28     return 1/true
     29   }
     30   return 0/false
     31 }
     32
     33 fn grapheme-stack-length _self: (addr grapheme-stack) -> _/eax: int {
     34   var self/esi: (addr grapheme-stack) <- copy _self
     35   var top/eax: (addr int) <- get self, top
     36   return *top
     37 }
     38
     39 fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme {
     40   var self/esi: (addr grapheme-stack) <- copy _self
     41   var top-addr/ecx: (addr int) <- get self, top
     42   var data-ah/edx: (addr handle array grapheme) <- get self, data
     43   var data/eax: (addr array grapheme) <- lookup *data-ah
     44   var top/edx: int <- copy *top-addr
     45   var dest-addr/edx: (addr grapheme) <- index data, top
     46   var val/eax: grapheme <- copy _val
     47   copy-to *dest-addr, val
     48   add-to *top-addr, 1
     49 }
     50
     51 fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: grapheme {
     52   var self/esi: (addr grapheme-stack) <- copy _self
     53   var top-addr/ecx: (addr int) <- get self, top
     54   {
     55     compare *top-addr, 0
     56     break-if->
     57     return -1
     58   }
     59   subtract-from *top-addr, 1
     60   var data-ah/edx: (addr handle array grapheme) <- get self, data
     61   var data/eax: (addr array grapheme) <- lookup *data-ah
     62   var top/edx: int <- copy *top-addr
     63   var result-addr/eax: (addr grapheme) <- index data, top
     64   return *result-addr
     65 }
     66
     67 fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) {
     68   var src/esi: (addr grapheme-stack) <- copy _src
     69   var data-ah/edi: (addr handle array grapheme) <- get src, data
     70   var _data/eax: (addr array grapheme) <- lookup *data-ah
     71   var data/edi: (addr array grapheme) <- copy _data
     72   var top-addr/ecx: (addr int) <- get src, top
     73   var i/eax: int <- copy 0
     74   {
     75     compare i, *top-addr
     76     break-if->=
     77     var g/edx: (addr grapheme) <- index data, i
     78     push-grapheme-stack dest, *g
     79     i <- increment
     80     loop
     81   }
     82 }
     83
     84 # dump stack to screen from bottom to top
     85 # colors hardcoded
     86 fn render-stack-from-bottom-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int -> _/eax: int, _/ecx: int {
     87   var self/esi: (addr grapheme-stack) <- copy _self
     88   var data-ah/edi: (addr handle array grapheme) <- get self, data
     89   var _data/eax: (addr array grapheme) <- lookup *data-ah
     90   var data/edi: (addr array grapheme) <- copy _data
     91   var x/eax: int <- copy _x
     92   var y/ecx: int <- copy _y
     93   var top-addr/edx: (addr int) <- get self, top
     94   var i/ebx: int <- copy 0
     95   {
     96     compare i, *top-addr
     97     break-if->=
     98     {
     99       var g/edx: (addr grapheme) <- index data, i
    100       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg
    101     }
    102     i <- increment
    103     loop
    104   }
    105   return x, y
    106 }
    107
    108 # helper for small words
    109 fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int -> _/eax: int {
    110   var _width/eax: int <- copy 0
    111   var _height/ecx: int <- copy 0
    112   _width, _height <- screen-size screen
    113   var width/edx: int <- copy _width
    114   var height/ebx: int <- copy _height
    115   var x2/eax: int <- copy 0
    116   var y2/ecx: int <- copy 0
    117   x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y
    118   return x2  # y2? yolo
    119 }
    120
    121 # dump stack to screen from top to bottom
    122 # optionally render a 'cursor' with the top grapheme
    123 fn render-stack-from-top-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, render-cursor?: boolean -> _/eax: int, _/ecx: int {
    124   var self/esi: (addr grapheme-stack) <- copy _self
    125   var data-ah/edi: (addr handle array grapheme) <- get self, data
    126   var _data/eax: (addr array grapheme) <- lookup *data-ah
    127   var data/edi: (addr array grapheme) <- copy _data
    128   var x/eax: int <- copy _x
    129   var y/ecx: int <- copy _y
    130   var top-addr/edx: (addr int) <- get self, top
    131   var i/ebx: int <- copy *top-addr
    132   i <- decrement
    133   # if render-cursor?, peel off first iteration
    134   {
    135     compare render-cursor?, 0/false
    136     break-if-=
    137     compare i, 0
    138     break-if-<
    139     {
    140       var g/edx: (addr grapheme) <- index data, i
    141       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 7/bg=cursor
    142     }
    143     i <- decrement
    144   }
    145   # remaining iterations
    146   {
    147     compare i, 0
    148     break-if-<
    149     {
    150       var g/edx: (addr grapheme) <- index data, i
    151       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg=cursor
    152     }
    153     i <- decrement
    154     loop
    155   }
    156   return x, y
    157 }
    158
    159 # helper for small words
    160 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
    161   var _width/eax: int <- copy 0
    162   var _height/ecx: int <- copy 0
    163   _width, _height <- screen-size screen
    164   var width/edx: int <- copy _width
    165   var height/ebx: int <- copy _height
    166   var x2/eax: int <- copy 0
    167   var y2/ecx: int <- copy 0
    168   x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, self, x, y, width, height, x, y, render-cursor?
    169   return x2  # y2? yolo
    170 }
    171
    172 fn test-render-grapheme-stack {
    173   # setup: gs = "abc"
    174   var gs-storage: grapheme-stack
    175   var gs/edi: (addr grapheme-stack) <- address gs-storage
    176   initialize-grapheme-stack gs, 5
    177   var g/eax: grapheme <- copy 0x61/a
    178   push-grapheme-stack gs, g
    179   g <- copy 0x62/b
    180   push-grapheme-stack gs, g
    181   g <- copy 0x63/c
    182   push-grapheme-stack gs, g
    183   # setup: screen
    184   var screen-on-stack: screen
    185   var screen/esi: (addr screen) <- address screen-on-stack
    186   initialize-screen screen, 5, 4
    187   #
    188   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y
    189   check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
    190   check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
    191   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "   ", "F - test-render-grapheme-stack from bottom: bg"
    192   #
    193   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
    194   check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
    195   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
    196   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "   ", "F - test-render-grapheme-stack from top without cursor: bg"
    197   #
    198   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
    199   check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
    200   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
    201   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|   ", "F - test-render-grapheme-stack from top with cursor: bg"
    202 }
    203
    204 # compare from bottom
    205 # beware: modifies 'stream', which must be disposed of after a false result
    206 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
    207   var self/esi: (addr grapheme-stack) <- copy _self
    208   var data-ah/edi: (addr handle array grapheme) <- get self, data
    209   var _data/eax: (addr array grapheme) <- lookup *data-ah
    210   var data/edi: (addr array grapheme) <- copy _data
    211   var top-addr/ecx: (addr int) <- get self, top
    212   var i/ebx: int <- copy 0
    213   {
    214     compare i, *top-addr
    215     break-if->=
    216     # if curr != expected, return false
    217     {
    218       var curr-a/edx: (addr grapheme) <- index data, i
    219       var expected/eax: grapheme <- read-grapheme s
    220       {
    221         compare expected, *curr-a
    222         break-if-=
    223         return 0/false
    224       }
    225     }
    226     i <- increment
    227     loop
    228   }
    229   return 1   # true
    230 }
    231
    232 # compare from bottom
    233 # beware: modifies 'stream', which must be disposed of after a false result
    234 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
    235   var self/esi: (addr grapheme-stack) <- copy _self
    236   var data-ah/edi: (addr handle array grapheme) <- get self, data
    237   var _data/eax: (addr array grapheme) <- lookup *data-ah
    238   var data/edi: (addr array grapheme) <- copy _data
    239   var top-addr/eax: (addr int) <- get self, top
    240   var i/ebx: int <- copy *top-addr
    241   i <- decrement
    242   {
    243     compare i, 0
    244     break-if-<
    245     {
    246       var curr-a/edx: (addr grapheme) <- index data, i
    247       var expected/eax: grapheme <- read-grapheme s
    248       # if curr != expected, return false
    249       {
    250         compare expected, *curr-a
    251         break-if-=
    252         return 0/false
    253       }
    254     }
    255     i <- decrement
    256     loop
    257   }
    258   return 1   # true
    259 }
    260
    261 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
    262   var self/esi: (addr grapheme-stack) <- copy _self
    263   var data-ah/eax: (addr handle array grapheme) <- get self, data
    264   var _data/eax: (addr array grapheme) <- lookup *data-ah
    265   var data/edx: (addr array grapheme) <- copy _data
    266   var top-addr/ecx: (addr int) <- get self, top
    267   var i/ebx: int <- copy 0
    268   var result/eax: boolean <- copy 1/true
    269   $grapheme-stack-is-integer?:loop: {
    270     compare i, *top-addr
    271     break-if->=
    272     var g/edx: (addr grapheme) <- index data, i
    273     result <- decimal-digit? *g
    274     compare result, 0/false
    275     break-if-=
    276     i <- increment
    277     loop
    278   }
    279   return result
    280 }