https://github.com/akkartik/mu/blob/main/linux/tile/gap-buffer.mu

      1 type gap-buffer {
      2   left: grapheme-stack
      3   right: grapheme-stack
      4 }
      5
      6 fn initialize-gap-buffer _self: (addr gap-buffer) {
      7   var self/esi: (addr gap-buffer) <- copy _self
      8   var left/eax: (addr grapheme-stack) <- get self, left
      9   initialize-grapheme-stack left, 0x10/max-word-size
     10   var right/eax: (addr grapheme-stack) <- get self, right
     11   initialize-grapheme-stack right, 0x10/max-word-size
     12 }
     13
     14 # just for tests
     15 fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) {
     16   initialize-gap-buffer self
     17   var stream-storage: (stream byte 0x10/max-word-size)
     18   var stream/ecx: (addr stream byte) <- address stream-storage
     19   write stream, s
     20   {
     21     var done?/eax: boolean <- stream-empty? stream
     22     compare done?, 0/false
     23     break-if-!=
     24     var g/eax: grapheme <- read-grapheme stream
     25     add-grapheme-at-gap self, g
     26     loop
     27   }
     28 }
     29
     30 fn gap-buffer-to-string self: (addr gap-buffer), out: (addr handle array byte) {
     31   var s-storage: (stream byte 0x100)
     32   var s/ecx: (addr stream byte) <- address s-storage
     33   emit-gap-buffer self, s
     34   stream-to-array s, out
     35 }
     36
     37 fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
     38   var self/esi: (addr gap-buffer) <- copy _self
     39   clear-stream out
     40   var left/eax: (addr grapheme-stack) <- get self, left
     41   emit-stack-from-bottom left, out
     42   var right/eax: (addr grapheme-stack) <- get self, right
     43   emit-stack-from-top right, out
     44 }
     45
     46 # dump stack from bottom to top
     47 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
     48   var self/esi: (addr grapheme-stack) <- copy _self
     49   var data-ah/edi: (addr handle array grapheme) <- get self, data
     50   var _data/eax: (addr array grapheme) <- lookup *data-ah
     51   var data/edi: (addr array grapheme) <- copy _data
     52   var top-addr/ecx: (addr int) <- get self, top
     53   var i/eax: int <- copy 0
     54   {
     55     compare i, *top-addr
     56     break-if->=
     57     var g/edx: (addr grapheme) <- index data, i
     58     write-grapheme out, *g
     59     i <- increment
     60     loop
     61   }
     62 }
     63
     64 # dump stack from top to bottom
     65 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
     66   var self/esi: (addr grapheme-stack) <- copy _self
     67   var data-ah/edi: (addr handle array grapheme) <- get self, data
     68   var _data/eax: (addr array grapheme) <- lookup *data-ah
     69   var data/edi: (addr array grapheme) <- copy _data
     70   var top-addr/ecx: (addr int) <- get self, top
     71   var i/eax: int <- copy *top-addr
     72   i <- decrement
     73   {
     74     compare i, 0
     75     break-if-<
     76     var g/edx: (addr grapheme) <- index data, i
     77     write-grapheme out, *g
     78     i <- decrement
     79     loop
     80   }
     81 }
     82
     83 fn render-gap-buffer screen: (addr screen), _gap: (addr gap-buffer) {
     84   var gap/esi: (addr gap-buffer) <- copy _gap
     85   var left/eax: (addr grapheme-stack) <- get gap, left
     86   render-stack-from-bottom left, screen
     87   var right/eax: (addr grapheme-stack) <- get gap, right
     88   render-stack-from-top right, screen
     89 }
     90
     91 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
     92   var gap/esi: (addr gap-buffer) <- copy _gap
     93   var left/eax: (addr grapheme-stack) <- get gap, left
     94   var tmp/eax: (addr int) <- get left, top
     95   var left-length/ecx: int <- copy *tmp
     96   var right/esi: (addr grapheme-stack) <- get gap, right
     97   tmp <- get right, top
     98   var result/eax: int <- copy *tmp
     99   result <- add left-length
    100   return result
    101 }
    102
    103 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
    104   var self/esi: (addr gap-buffer) <- copy _self
    105   var left/eax: (addr grapheme-stack) <- get self, left
    106   push-grapheme-stack left, g
    107 }
    108
    109 fn gap-to-start self: (addr gap-buffer) {
    110   {
    111     var curr/eax: grapheme <- gap-left self
    112     compare curr, -1
    113     loop-if-!=
    114   }
    115 }
    116
    117 fn gap-to-end self: (addr gap-buffer) {
    118   {
    119     var curr/eax: grapheme <- gap-right self
    120     compare curr, -1
    121     loop-if-!=
    122   }
    123 }
    124
    125 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
    126   var self/esi: (addr gap-buffer) <- copy _self
    127   var left/eax: (addr grapheme-stack) <- get self, left
    128   var result/eax: boolean <- grapheme-stack-empty? left
    129   return result
    130 }
    131
    132 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
    133   var self/esi: (addr gap-buffer) <- copy _self
    134   var right/eax: (addr grapheme-stack) <- get self, right
    135   var result/eax: boolean <- grapheme-stack-empty? right
    136   return result
    137 }
    138
    139 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
    140   var self/esi: (addr gap-buffer) <- copy _self
    141   var g/eax: grapheme <- copy 0
    142   var right/ecx: (addr grapheme-stack) <- get self, right
    143   g <- pop-grapheme-stack right
    144   compare g, -1
    145   {
    146     break-if-=
    147     var left/ecx: (addr grapheme-stack) <- get self, left
    148     push-grapheme-stack left, g
    149   }
    150   return g
    151 }
    152
    153 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
    154   var self/esi: (addr gap-buffer) <- copy _self
    155   var g/eax: grapheme <- copy 0
    156   {
    157     var left/ecx: (addr grapheme-stack) <- get self, left
    158     g <- pop-grapheme-stack left
    159   }
    160   compare g, -1
    161   {
    162     break-if-=
    163     var right/ecx: (addr grapheme-stack) <- get self, right
    164     push-grapheme-stack right, g
    165   }
    166   return g
    167 }
    168
    169 fn gap-index _self: (addr gap-buffer) -> _/eax: int {
    170   var self/eax: (addr gap-buffer) <- copy _self
    171   var left/eax: (addr grapheme-stack) <- get self, left
    172   var top-addr/eax: (addr int) <- get left, top
    173   var result/eax: int <- copy *top-addr
    174   return result
    175 }
    176
    177 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
    178   var self/esi: (addr gap-buffer) <- copy _self
    179   # try to read from left
    180   var left/eax: (addr grapheme-stack) <- get self, left
    181   var top-addr/ecx: (addr int) <- get left, top
    182   compare *top-addr, 0
    183   {
    184     break-if-<=
    185     var data-ah/eax: (addr handle array grapheme) <- get left, data
    186     var data/eax: (addr array grapheme) <- lookup *data-ah
    187     var result-addr/eax: (addr grapheme) <- index data, 0
    188     return *result-addr
    189   }
    190   # try to read from right
    191   var right/eax: (addr grapheme-stack) <- get self, right
    192   top-addr <- get right, top
    193   compare *top-addr, 0
    194   {
    195     break-if-<=
    196     var data-ah/eax: (addr handle array grapheme) <- get right, data
    197     var data/eax: (addr array grapheme) <- lookup *data-ah
    198     var top/ecx: int <- copy *top-addr
    199     top <- decrement
    200     var result-addr/eax: (addr grapheme) <- index data, top
    201     return *result-addr
    202   }
    203   # give up
    204   return -1
    205 }
    206
    207 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
    208   var self/esi: (addr gap-buffer) <- copy _self
    209   # try to read from left
    210   var left/ecx: (addr grapheme-stack) <- get self, left
    211   var top-addr/edx: (addr int) <- get left, top
    212   compare *top-addr, 0
    213   {
    214     break-if-<=
    215     var result/eax: grapheme <- pop-grapheme-stack left
    216     push-grapheme-stack left, result
    217     return result
    218   }
    219   # give up
    220   return -1
    221 }
    222
    223 fn delete-before-gap _self: (addr gap-buffer) {
    224   var self/eax: (addr gap-buffer) <- copy _self
    225   var left/eax: (addr grapheme-stack) <- get self, left
    226   var dummy/eax: grapheme <- pop-grapheme-stack left
    227 }
    228
    229 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
    230   var self/eax: (addr gap-buffer) <- copy _self
    231   var right/eax: (addr grapheme-stack) <- get self, right
    232   var result/eax: grapheme <- pop-grapheme-stack right
    233   return result
    234 }
    235
    236 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
    237   var self/esi: (addr gap-buffer) <- copy _self
    238   # complication: graphemes may be multiple bytes
    239   # so don't rely on length
    240   # instead turn the expected result into a stream and arrange to read from it in order
    241   var stream-storage: (stream byte 0x10/max-word-size)
    242   var expected-stream/ecx: (addr stream byte) <- address stream-storage
    243   write expected-stream, s
    244   # compare left
    245   var left/edx: (addr grapheme-stack) <- get self, left
    246   var result/eax: boolean <- prefix-match? left, expected-stream
    247   compare result, 0/false
    248   {
    249     break-if-!=
    250     return result
    251   }
    252   # compare right
    253   var right/edx: (addr grapheme-stack) <- get self, right
    254   result <- suffix-match? right, expected-stream
    255   compare result, 0/false
    256   {
    257     break-if-!=
    258     return result
    259   }
    260   # ensure there's nothing left over
    261   result <- stream-empty? expected-stream
    262   return result
    263 }
    264
    265 fn test-gap-buffer-equal-from-end? {
    266   var _g: gap-buffer
    267   var g/esi: (addr gap-buffer) <- address _g
    268   initialize-gap-buffer g
    269   #
    270   var c/eax: grapheme <- copy 0x61/a
    271   add-grapheme-at-gap g, c
    272   add-grapheme-at-gap g, c
    273   add-grapheme-at-gap g, c
    274   # gap is at end (right is empty)
    275   var _result/eax: boolean <- gap-buffer-equal? g, "aaa"
    276   var result/eax: int <- copy _result
    277   check-ints-equal result, 1, "F - test-gap-buffer-equal-from-end?"
    278 }
    279
    280 fn test-gap-buffer-equal-from-middle? {
    281   var _g: gap-buffer
    282   var g/esi: (addr gap-buffer) <- address _g
    283   initialize-gap-buffer g
    284   #
    285   var c/eax: grapheme <- copy 0x61/a
    286   add-grapheme-at-gap g, c
    287   add-grapheme-at-gap g, c
    288   add-grapheme-at-gap g, c
    289   var dummy/eax: grapheme <- gap-left g
    290   # gap is in the middle
    291   var _result/eax: boolean <- gap-buffer-equal? g, "aaa"
    292   var result/eax: int <- copy _result
    293   check-ints-equal result, 1, "F - test-gap-buffer-equal-from-middle?"
    294 }
    295
    296 fn test-gap-buffer-equal-from-start? {
    297   var _g: gap-buffer
    298   var g/esi: (addr gap-buffer) <- address _g
    299   initialize-gap-buffer g
    300   #
    301   var c/eax: grapheme <- copy 0x61/a
    302   add-grapheme-at-gap g, c
    303   add-grapheme-at-gap g, c
    304   add-grapheme-at-gap g, c
    305   var dummy/eax: grapheme <- gap-left g
    306   dummy <- gap-left g
    307   dummy <- gap-left g
    308   # gap is at the start
    309   var _result/eax: boolean <- gap-buffer-equal? g, "aaa"
    310   var result/eax: int <- copy _result
    311   check-ints-equal result, 1, "F - test-gap-buffer-equal-from-start?"
    312 }
    313
    314 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
    315   # obtain src-a, dest-a
    316   var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
    317   var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
    318   var src-a/esi: (addr gap-buffer) <- copy _src-a
    319   var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
    320   var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
    321   var dest-a/edi: (addr gap-buffer) <- copy _dest-a
    322   # copy left grapheme-stack
    323   var src/ecx: (addr grapheme-stack) <- get src-a, left
    324   var dest/edx: (addr grapheme-stack) <- get dest-a, left
    325   copy-grapheme-stack src, dest
    326   # copy right grapheme-stack
    327   src <- get src-a, right
    328   dest <- get dest-a, right
    329   copy-grapheme-stack src, dest
    330 }
    331
    332 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
    333   var self/esi: (addr gap-buffer) <- copy _self
    334   var curr/ecx: (addr grapheme-stack) <- get self, left
    335   var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
    336   {
    337     compare result, 0/false
    338     break-if-=
    339     curr <- get self, right
    340     result <- grapheme-stack-is-decimal-integer? curr
    341   }
    342   return result
    343 }