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 # hardcoded colors:
     86 #   matching paren
     87 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, highlight-matching-open-paren?: boolean, open-paren-depth: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
     88   var self/esi: (addr grapheme-stack) <- copy _self
     89   var matching-open-paren-index/edx: int <- get-matching-open-paren-index self, highlight-matching-open-paren?, open-paren-depth
     90   var data-ah/edi: (addr handle array grapheme) <- get self, data
     91   var _data/eax: (addr array grapheme) <- lookup *data-ah
     92   var data/edi: (addr array grapheme) <- copy _data
     93   var x/eax: int <- copy _x
     94   var y/ecx: int <- copy _y
     95   var top-addr/esi: (addr int) <- get self, top
     96   var i/ebx: int <- copy 0
     97   {
     98     compare i, *top-addr
     99     break-if->=
    100     {
    101       var g/esi: (addr grapheme) <- index data, i
    102       var fg: int
    103       {
    104         var tmp/eax: int <- copy color
    105         copy-to fg, tmp
    106       }
    107       {
    108         compare i, matching-open-paren-index
    109         break-if-!=
    110         copy-to fg, 0xf/highlight
    111       }
    112       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color
    113     }
    114     i <- increment
    115     loop
    116   }
    117   return x, y
    118 }
    119
    120 # helper for small words
    121 fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, highlight-matching-open-paren?: boolean, open-paren-depth: int -> _/eax: int {
    122   var _width/eax: int <- copy 0
    123   var _height/ecx: int <- copy 0
    124   _width, _height <- screen-size screen
    125   var width/edx: int <- copy _width
    126   var height/ebx: int <- copy _height
    127   var x2/eax: int <- copy 0
    128   var y2/ecx: int <- copy 0
    129   x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y, highlight-matching-open-paren?, open-paren-depth, 3/fg=cyan, 0xc5/bg=blue-bg
    130   return x2  # y2? yolo
    131 }
    132
    133 # dump stack to screen from top to bottom
    134 # optionally render a 'cursor' with the top grapheme
    135 # hard-coded colors:
    136 #   matching paren
    137 #   cursor
    138 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, color: int, background-color: int -> _/eax: int, _/ecx: int {
    139   var self/esi: (addr grapheme-stack) <- copy _self
    140   var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor?
    141   var data-ah/eax: (addr handle array grapheme) <- get self, data
    142   var _data/eax: (addr array grapheme) <- lookup *data-ah
    143   var data/edi: (addr array grapheme) <- copy _data
    144   var x/eax: int <- copy _x
    145   var y/ecx: int <- copy _y
    146   var top-addr/ebx: (addr int) <- get self, top
    147   var i/ebx: int <- copy *top-addr
    148   i <- decrement
    149   # if render-cursor?, peel off first iteration
    150   {
    151     compare render-cursor?, 0/false
    152     break-if-=
    153     compare i, 0
    154     break-if-<
    155     var g/esi: (addr grapheme) <- index data, i
    156     x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, color, 7/bg=cursor
    157     i <- decrement
    158   }
    159   # remaining iterations
    160   {
    161     compare i, 0
    162     break-if-<
    163     # highlight matching paren if needed
    164     var fg: int
    165     {
    166       var tmp/eax: int <- copy color
    167       copy-to fg, tmp
    168     }
    169     compare i, matching-close-paren-index
    170     {
    171       break-if-!=
    172       copy-to fg, 0xf/highlight
    173     }
    174     #
    175     var g/esi: (addr grapheme) <- index data, i
    176     x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color
    177     i <- decrement
    178     loop
    179   }
    180   return x, y
    181 }
    182
    183 # helper for small words
    184 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
    185   var _width/eax: int <- copy 0
    186   var _height/ecx: int <- copy 0
    187   _width, _height <- screen-size screen
    188   var width/edx: int <- copy _width
    189   var height/ebx: int <- copy _height
    190   var x2/eax: int <- copy 0
    191   var y2/ecx: int <- copy 0
    192   x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, self, x, y, width, height, x, y, render-cursor?, 3/fg=cyan, 0xc5/bg=blue-bg
    193   return x2  # y2? yolo
    194 }
    195
    196 fn test-render-grapheme-stack {
    197   # setup: gs = "abc"
    198   var gs-storage: grapheme-stack
    199   var gs/edi: (addr grapheme-stack) <- address gs-storage
    200   initialize-grapheme-stack gs, 5
    201   var g/eax: grapheme <- copy 0x61/a
    202   push-grapheme-stack gs, g
    203   g <- copy 0x62/b
    204   push-grapheme-stack gs, g
    205   g <- copy 0x63/c
    206   push-grapheme-stack gs, g
    207   # setup: screen
    208   var screen-on-stack: screen
    209   var screen/esi: (addr screen) <- address screen-on-stack
    210   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    211   #
    212   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth
    213   check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
    214   check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
    215   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "   ", "F - test-render-grapheme-stack from bottom: bg"
    216   #
    217   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
    218   check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
    219   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
    220   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "   ", "F - test-render-grapheme-stack from top without cursor: bg"
    221   #
    222   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
    223   check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
    224   check-ints-equal x, 3, "F - test-render-grapheme-stack from top with cursor: result"
    225   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|   ", "F - test-render-grapheme-stack from top with cursor: bg"
    226 }
    227
    228 fn test-render-grapheme-stack-while-highlighting-matching-close-paren {
    229   # setup: gs = "(b)"
    230   var gs-storage: grapheme-stack
    231   var gs/edi: (addr grapheme-stack) <- address gs-storage
    232   initialize-grapheme-stack gs, 5
    233   var g/eax: grapheme <- copy 0x29/close-paren
    234   push-grapheme-stack gs, g
    235   g <- copy 0x62/b
    236   push-grapheme-stack gs, g
    237   g <- copy 0x28/open-paren
    238   push-grapheme-stack gs, g
    239   # setup: screen
    240   var screen-on-stack: screen
    241   var screen/esi: (addr screen) <- address screen-on-stack
    242   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    243   #
    244   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
    245   check-screen-row                      screen,               2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren"
    246   check-background-color-in-screen-row  screen, 7/bg=cursor,  2/y, "|   ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor"
    247   check-screen-row-in-color             screen, 0xf/fg=white, 2/y, "  ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren"
    248 }
    249
    250 fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 {
    251   # setup: gs = "(a (b)) c"
    252   var gs-storage: grapheme-stack
    253   var gs/edi: (addr grapheme-stack) <- address gs-storage
    254   initialize-grapheme-stack gs, 0x10
    255   var g/eax: grapheme <- copy 0x63/c
    256   push-grapheme-stack gs, g
    257   g <- copy 0x20/space
    258   push-grapheme-stack gs, g
    259   g <- copy 0x29/close-paren
    260   push-grapheme-stack gs, g
    261   g <- copy 0x29/close-paren
    262   push-grapheme-stack gs, g
    263   g <- copy 0x62/b
    264   push-grapheme-stack gs, g
    265   g <- copy 0x28/open-paren
    266   push-grapheme-stack gs, g
    267   g <- copy 0x20/space
    268   push-grapheme-stack gs, g
    269   g <- copy 0x61/a
    270   push-grapheme-stack gs, g
    271   g <- copy 0x28/open-paren
    272   push-grapheme-stack gs, g
    273   # setup: screen
    274   var screen-on-stack: screen
    275   var screen/esi: (addr screen) <- address screen-on-stack
    276   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    277   #
    278   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
    279   check-screen-row                      screen,               2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2"
    280   check-background-color-in-screen-row  screen, 7/bg=cursor,  2/y, "|         ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor"
    281   check-screen-row-in-color             screen, 0xf/fg=white, 2/y, "      )   ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren"
    282 }
    283
    284 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end {
    285   # setup: gs = "(b)"
    286   var gs-storage: grapheme-stack
    287   var gs/edi: (addr grapheme-stack) <- address gs-storage
    288   initialize-grapheme-stack gs, 5
    289   var g/eax: grapheme <- copy 0x28/open-paren
    290   push-grapheme-stack gs, g
    291   g <- copy 0x62/b
    292   push-grapheme-stack gs, g
    293   g <- copy 0x29/close-paren
    294   push-grapheme-stack gs, g
    295   # setup: screen
    296   var screen-on-stack: screen
    297   var screen/esi: (addr screen) <- address screen-on-stack
    298   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    299   #
    300   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
    301   check-screen-row          screen,               2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end"
    302   check-screen-row-in-color screen, 0xf/fg=white, 2/y, "(   ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end: matching paren"
    303 }
    304
    305 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 {
    306   # setup: gs = "a((b))"
    307   var gs-storage: grapheme-stack
    308   var gs/edi: (addr grapheme-stack) <- address gs-storage
    309   initialize-grapheme-stack gs, 0x10
    310   var g/eax: grapheme <- copy 0x61/a
    311   push-grapheme-stack gs, g
    312   g <- copy 0x28/open-paren
    313   push-grapheme-stack gs, g
    314   g <- copy 0x28/open-paren
    315   push-grapheme-stack gs, g
    316   g <- copy 0x62/b
    317   push-grapheme-stack gs, g
    318   g <- copy 0x29/close-paren
    319   push-grapheme-stack gs, g
    320   g <- copy 0x29/close-paren
    321   push-grapheme-stack gs, g
    322   # setup: screen
    323   var screen-on-stack: screen
    324   var screen/esi: (addr screen) <- address screen-on-stack
    325   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    326   #
    327   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
    328   check-screen-row          screen,               2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2"
    329   check-screen-row-in-color screen, 0xf/fg=white, 2/y, " (     ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2: matching paren"
    330 }
    331
    332 fn test-render-grapheme-stack-while-highlighting-matching-open-paren {
    333   # setup: gs = "(b"
    334   var gs-storage: grapheme-stack
    335   var gs/edi: (addr grapheme-stack) <- address gs-storage
    336   initialize-grapheme-stack gs, 5
    337   var g/eax: grapheme <- copy 0x28/open-paren
    338   push-grapheme-stack gs, g
    339   g <- copy 0x62/b
    340   push-grapheme-stack gs, g
    341   # setup: screen
    342   var screen-on-stack: screen
    343   var screen/esi: (addr screen) <- address screen-on-stack
    344   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    345   #
    346   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
    347   check-screen-row          screen,               2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren"
    348   check-screen-row-in-color screen, 0xf/fg=white, 2/y, "(  ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren"
    349 }
    350
    351 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 {
    352   # setup: gs = "a((b)"
    353   var gs-storage: grapheme-stack
    354   var gs/edi: (addr grapheme-stack) <- address gs-storage
    355   initialize-grapheme-stack gs, 0x10
    356   var g/eax: grapheme <- copy 0x61/a
    357   push-grapheme-stack gs, g
    358   g <- copy 0x28/open-paren
    359   push-grapheme-stack gs, g
    360   g <- copy 0x28/open-paren
    361   push-grapheme-stack gs, g
    362   g <- copy 0x62/b
    363   push-grapheme-stack gs, g
    364   g <- copy 0x29/close-paren
    365   push-grapheme-stack gs, g
    366   # setup: screen
    367   var screen-on-stack: screen
    368   var screen/esi: (addr screen) <- address screen-on-stack
    369   initialize-screen screen, 5, 4, 0/no-pixel-graphics
    370   #
    371   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
    372   check-screen-row          screen,               2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2"
    373   check-screen-row-in-color screen, 0xf/fg=white, 2/y, " (    ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren"
    374 }
    375
    376 # return the index of the matching close-paren of the grapheme at cursor (top of stack)
    377 # or top index if there's no matching close-paren
    378 fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: boolean -> _/edx: int {
    379   var self/esi: (addr grapheme-stack) <- copy _self
    380   var top-addr/edx: (addr int) <- get self, top
    381   # if not rendering cursor, return
    382   compare render-cursor?, 0/false
    383   {
    384     break-if-!=
    385     return *top-addr
    386   }
    387   var data-ah/eax: (addr handle array grapheme) <- get self, data
    388   var data/eax: (addr array grapheme) <- lookup *data-ah
    389   var i/ecx: int <- copy *top-addr
    390   # if stack is empty, return
    391   compare i, 0
    392   {
    393     break-if->
    394     return *top-addr
    395   }
    396   # if cursor is not '(' return
    397   i <- decrement
    398   var g/esi: (addr grapheme) <- index data, i
    399   compare *g, 0x28/open-paren
    400   {
    401     break-if-=
    402     return *top-addr
    403   }
    404   # otherwise scan to matching paren
    405   var paren-count/ebx: int <- copy 1
    406   i <- decrement
    407   {
    408     compare i, 0
    409     break-if-<
    410     var g/esi: (addr grapheme) <- index data, i
    411     compare *g, 0x28/open-paren
    412     {
    413       break-if-!=
    414       paren-count <- increment
    415     }
    416     compare *g, 0x29/close-paren
    417     {
    418       break-if-!=
    419       compare paren-count, 1
    420       {
    421         break-if-!=
    422         return i
    423       }
    424       paren-count <- decrement
    425     }
    426     i <- decrement
    427     loop
    428   }
    429   return *top-addr
    430 }
    431
    432 # return the index of the first open-paren at the given depth
    433 # or top index if there's no matching close-paren
    434 fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int {
    435   var self/esi: (addr grapheme-stack) <- copy _self
    436   var top-addr/edx: (addr int) <- get self, top
    437   # if not rendering cursor, return
    438   compare control, 0/false
    439   {
    440     break-if-!=
    441     return *top-addr
    442   }
    443   var data-ah/eax: (addr handle array grapheme) <- get self, data
    444   var data/eax: (addr array grapheme) <- lookup *data-ah
    445   var i/ecx: int <- copy *top-addr
    446   # if stack is empty, return
    447   compare i, 0
    448   {
    449     break-if->
    450     return *top-addr
    451   }
    452   # scan to matching open paren
    453   var paren-count/ebx: int <- copy 0
    454   i <- decrement
    455   {
    456     compare i, 0
    457     break-if-<
    458     var g/esi: (addr grapheme) <- index data, i
    459     compare *g, 0x29/close-paren
    460     {
    461       break-if-!=
    462       paren-count <- increment
    463     }
    464     compare *g, 0x28/open-paren
    465     {
    466       break-if-!=
    467       compare paren-count, depth
    468       {
    469         break-if-!=
    470         return i
    471       }
    472       paren-count <- decrement
    473     }
    474     i <- decrement
    475     loop
    476   }
    477   return *top-addr
    478 }
    479
    480 # compare from bottom
    481 # beware: modifies 'stream', which must be disposed of after a false result
    482 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
    483   var self/esi: (addr grapheme-stack) <- copy _self
    484   var data-ah/edi: (addr handle array grapheme) <- get self, data
    485   var _data/eax: (addr array grapheme) <- lookup *data-ah
    486   var data/edi: (addr array grapheme) <- copy _data
    487   var top-addr/ecx: (addr int) <- get self, top
    488   var i/ebx: int <- copy 0
    489   {
    490     compare i, *top-addr
    491     break-if->=
    492     # if curr != expected, return false
    493     {
    494       var curr-a/edx: (addr grapheme) <- index data, i
    495       var expected/eax: grapheme <- read-grapheme s
    496       {
    497         compare expected, *curr-a
    498         break-if-=
    499         return 0/false
    500       }
    501     }
    502     i <- increment
    503     loop
    504   }
    505   return 1   # true
    506 }
    507
    508 # compare from bottom
    509 # beware: modifies 'stream', which must be disposed of after a false result
    510 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
    511   var self/esi: (addr grapheme-stack) <- copy _self
    512   var data-ah/edi: (addr handle array grapheme) <- get self, data
    513   var _data/eax: (addr array grapheme) <- lookup *data-ah
    514   var data/edi: (addr array grapheme) <- copy _data
    515   var top-addr/eax: (addr int) <- get self, top
    516   var i/ebx: int <- copy *top-addr
    517   i <- decrement
    518   {
    519     compare i, 0
    520     break-if-<
    521     {
    522       var curr-a/edx: (addr grapheme) <- index data, i
    523       var expected/eax: grapheme <- read-grapheme s
    524       # if curr != expected, return false
    525       {
    526         compare expected, *curr-a
    527         break-if-=
    528         return 0/false
    529       }
    530     }
    531     i <- decrement
    532     loop
    533   }
    534   return 1   # true
    535 }
    536
    537 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
    538   var self/esi: (addr grapheme-stack) <- copy _self
    539   var data-ah/eax: (addr handle array grapheme) <- get self, data
    540   var _data/eax: (addr array grapheme) <- lookup *data-ah
    541   var data/edx: (addr array grapheme) <- copy _data
    542   var top-addr/ecx: (addr int) <- get self, top
    543   var i/ebx: int <- copy 0
    544   var result/eax: boolean <- copy 1/true
    545   $grapheme-stack-is-integer?:loop: {
    546     compare i, *top-addr
    547     break-if->=
    548     var g/edx: (addr grapheme) <- index data, i
    549     result <- decimal-digit? *g
    550     compare result, 0/false
    551     break-if-=
    552     i <- increment
    553     loop
    554   }
    555   return result
    556 }