https://github.com/akkartik/mu/blob/main/shell/sandbox.mu

      1 type sandbox {
      2   data: (handle gap-buffer)
      3   value: (handle stream byte)
      4   screen-var: (handle cell)
      5   keyboard-var: (handle cell)
      6   trace: (handle trace)
      7   cursor-in-data?: boolean
      8   cursor-in-keyboard?: boolean
      9   cursor-in-trace?: boolean
     10 }
     11
     12 fn initialize-sandbox _self: (addr sandbox), fake-screen-and-keyboard?: boolean {
     13   var self/esi: (addr sandbox) <- copy _self
     14   var data-ah/eax: (addr handle gap-buffer) <- get self, data
     15   allocate data-ah
     16   var data/eax: (addr gap-buffer) <- lookup *data-ah
     17   initialize-gap-buffer data, 0x1000/4KB
     18   #
     19   var value-ah/eax: (addr handle stream byte) <- get self, value
     20   populate-stream value-ah, 0x1000/4KB
     21   #
     22   {
     23     compare fake-screen-and-keyboard?, 0/false
     24     break-if-=
     25     var screen-ah/eax: (addr handle cell) <- get self, screen-var
     26     new-fake-screen screen-ah, 8/width, 3/height, 1/enable-pixel-graphics
     27     var keyboard-ah/eax: (addr handle cell) <- get self, keyboard-var
     28     new-fake-keyboard keyboard-ah, 0x10/keyboard-capacity
     29   }
     30   #
     31   var trace-ah/eax: (addr handle trace) <- get self, trace
     32   allocate trace-ah
     33   var trace/eax: (addr trace) <- lookup *trace-ah
     34   initialize-trace trace, 0x8000/lines, 0x80/visible-lines
     35   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
     36   copy-to *cursor-in-data?, 1/true
     37 }
     38
     39 ## some helpers for tests
     40
     41 fn initialize-sandbox-with _self: (addr sandbox), s: (addr array byte) {
     42   var self/esi: (addr sandbox) <- copy _self
     43   var data-ah/eax: (addr handle gap-buffer) <- get self, data
     44   allocate data-ah
     45   var data/eax: (addr gap-buffer) <- lookup *data-ah
     46   initialize-gap-buffer-with data, s
     47   var value-ah/eax: (addr handle stream byte) <- get self, value
     48   populate-stream value-ah, 0x1000/4KB
     49   var trace-ah/eax: (addr handle trace) <- get self, trace
     50   allocate trace-ah
     51   var trace/eax: (addr trace) <- lookup *trace-ah
     52   initialize-trace trace, 0x8000/lines, 0x80/visible-lines
     53   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
     54   copy-to *cursor-in-data?, 1/true
     55 }
     56
     57 fn allocate-sandbox-with _out: (addr handle sandbox), s: (addr array byte) {
     58   var out/eax: (addr handle sandbox) <- copy _out
     59   allocate out
     60   var out-addr/eax: (addr sandbox) <- lookup *out
     61   initialize-sandbox-with out-addr, s
     62 }
     63
     64 fn write-sandbox out: (addr stream byte), _self: (addr sandbox) {
     65   var self/eax: (addr sandbox) <- copy _self
     66   var data-ah/eax: (addr handle gap-buffer) <- get self, data
     67   var data/eax: (addr gap-buffer) <- lookup *data-ah
     68   {
     69     var len/eax: int <- gap-buffer-length data
     70     compare len, 0
     71     break-if-!=
     72     return
     73   }
     74   write out, "  (sandbox . "
     75   append-gap-buffer data, out
     76   write out, ")\n"
     77 }
     78
     79 ##
     80
     81 fn render-sandbox screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int {
     82   clear-rect screen, xmin, ymin, xmax, ymax, 0xc5/bg=blue-bg=black
     83   add-to xmin, 1/padding-left
     84   add-to ymin, 1/padding-top
     85   subtract-from xmax, 1/padding-right
     86   var self/esi: (addr sandbox) <- copy _self
     87   # data
     88   var data-ah/eax: (addr handle gap-buffer) <- get self, data
     89   var _data/eax: (addr gap-buffer) <- lookup *data-ah
     90   var data/edx: (addr gap-buffer) <- copy _data
     91   var x/eax: int <- copy xmin
     92   var y/ecx: int <- copy ymin
     93   y <- maybe-render-empty-screen screen, self, xmin, y
     94   y <- maybe-render-keyboard screen, self, xmin, y
     95   var cursor-in-sandbox?/ebx: (addr boolean) <- get self, cursor-in-data?
     96   x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, *cursor-in-sandbox?, 7/fg, 0xc5/bg=blue-bg
     97   y <- increment
     98   # trace
     99   var trace-ah/eax: (addr handle trace) <- get self, trace
    100   var _trace/eax: (addr trace) <- lookup *trace-ah
    101   var trace/edx: (addr trace) <- copy _trace
    102   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
    103   y <- render-trace screen, trace, xmin, y, xmax, ymax, *cursor-in-trace?
    104   # value
    105   $render-sandbox:value: {
    106     var value-ah/eax: (addr handle stream byte) <- get self, value
    107     var _value/eax: (addr stream byte) <- lookup *value-ah
    108     var value/esi: (addr stream byte) <- copy _value
    109     rewind-stream value
    110     var done?/eax: boolean <- stream-empty? value
    111     compare done?, 0/false
    112     break-if-!=
    113     var x/eax: int <- copy 0
    114     x, y <- draw-text-wrapping-right-then-down screen, "=> ", xmin, y, xmax, ymax, xmin, y, 7/fg, 0xc5/bg=blue-bg
    115     var x2/edx: int <- copy x
    116     var dummy/eax: int <- draw-stream-rightward screen, value, x2, xmax, y, 7/fg=grey, 0xc5/bg=blue-bg
    117   }
    118   y <- add 2  # padding
    119   y <- maybe-render-screen screen, self, xmin, y
    120   # render menu
    121   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
    122   compare *cursor-in-data?, 0/false
    123   {
    124     break-if-=
    125     render-sandbox-menu screen, self
    126     return
    127   }
    128   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
    129   compare *cursor-in-trace?, 0/false
    130   {
    131     break-if-=
    132     render-trace-menu screen
    133     return
    134   }
    135   var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
    136   compare *cursor-in-keyboard?, 0/false
    137   {
    138     break-if-=
    139     render-keyboard-menu screen
    140     return
    141   }
    142 }
    143
    144 fn clear-sandbox-output screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int {
    145   # render just enough of the sandbox to figure out what to erase
    146   var self/esi: (addr sandbox) <- copy _self
    147   var data-ah/eax: (addr handle gap-buffer) <- get self, data
    148   var _data/eax: (addr gap-buffer) <- lookup *data-ah
    149   var data/edx: (addr gap-buffer) <- copy _data
    150   var x/eax: int <- copy xmin
    151   var y/ecx: int <- copy ymin
    152   y <- maybe-render-empty-screen screen, self, xmin, y
    153   y <- maybe-render-keyboard screen, self, xmin, y
    154   var cursor-in-sandbox?/ebx: (addr boolean) <- get self, cursor-in-data?
    155   x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, *cursor-in-sandbox?, 3/fg, 0xc5/bg=blue-bg
    156   y <- increment
    157   clear-rect screen, xmin, y, xmax, ymax, 0xc5/bg=blue-bg=black
    158 }
    159
    160 fn maybe-render-empty-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
    161   var self/esi: (addr sandbox) <- copy _self
    162   var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
    163   var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
    164   compare screen-obj-cell, 0
    165   {
    166     break-if-!=
    167     return ymin
    168   }
    169   var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
    170   compare *screen-obj-cell-type, 5/screen
    171   {
    172     break-if-=
    173     return ymin  # silently give up on rendering the screen
    174   }
    175   var y/ecx: int <- copy ymin
    176   var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
    177   var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
    178   var screen-obj/edx: (addr screen) <- copy _screen-obj
    179   var x/eax: int <- draw-text-rightward screen, "screen:   ", xmin, 0x99/xmax, y, 7/fg, 0xc5/bg=blue-bg
    180   y <- render-empty-screen screen, screen-obj, x, y
    181   return y
    182 }
    183
    184 fn maybe-render-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
    185   var self/esi: (addr sandbox) <- copy _self
    186   var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
    187   var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
    188   compare screen-obj-cell, 0
    189   {
    190     break-if-!=
    191     return ymin
    192   }
    193   var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
    194   compare *screen-obj-cell-type, 5/screen
    195   {
    196     break-if-=
    197     return ymin  # silently give up on rendering the screen
    198   }
    199   var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
    200   var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
    201   var screen-obj/edx: (addr screen) <- copy _screen-obj
    202   {
    203     var screen-empty?/eax: boolean <- fake-screen-empty? screen-obj
    204     compare screen-empty?, 0/false
    205     break-if-=
    206     return ymin
    207   }
    208   var x/eax: int <- draw-text-rightward screen, "screen:   ", xmin, 0x99/xmax, ymin, 7/fg, 0xc5/bg=blue-bg
    209   var y/ecx: int <- copy ymin
    210   y <- render-screen screen, screen-obj, x, y
    211   return y
    212 }
    213
    214 fn render-empty-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
    215   var target-screen/esi: (addr screen) <- copy _target-screen
    216   var screen-y/edi: int <- copy ymin
    217   # screen
    218   var height/edx: (addr int) <- get target-screen, height
    219   var y/ecx: int <- copy 0
    220   {
    221     compare y, *height
    222     break-if->=
    223     set-cursor-position screen, xmin, screen-y
    224     var width/edx: (addr int) <- get target-screen, width
    225     var x/ebx: int <- copy 0
    226     {
    227       compare x, *width
    228       break-if->=
    229       draw-code-point-at-cursor screen, 0x20/space, 0x18/fg, 0/bg
    230       move-cursor-right screen
    231       x <- increment
    232       loop
    233     }
    234     y <- increment
    235     screen-y <- increment
    236     loop
    237   }
    238   return screen-y
    239 }
    240
    241 fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
    242   var target-screen/esi: (addr screen) <- copy _target-screen
    243   var screen-y/edi: int <- copy ymin
    244   # text data
    245   {
    246     var height/edx: (addr int) <- get target-screen, height
    247     var y/ecx: int <- copy 0
    248     {
    249       compare y, *height
    250       break-if->=
    251       set-cursor-position screen, xmin, screen-y
    252       var width/edx: (addr int) <- get target-screen, width
    253       var x/ebx: int <- copy 0
    254       {
    255         compare x, *width
    256         break-if->=
    257         print-screen-cell-of-fake-screen screen, target-screen, x, y
    258         move-cursor-right screen
    259         x <- increment
    260         loop
    261       }
    262       y <- increment
    263       screen-y <- increment
    264       loop
    265     }
    266   }
    267   # pixel data
    268   {
    269     # screen top left pixels x y width height
    270     var tmp/eax: int <- copy xmin
    271     tmp <- shift-left 3/log2-font-width
    272     var left: int
    273     copy-to left, tmp
    274     tmp <- copy ymin
    275     tmp <- shift-left 4/log2-font-height
    276     var top: int
    277     copy-to top, tmp
    278     var pixels-ah/eax: (addr handle array byte) <- get target-screen, pixels
    279     var _pixels/eax: (addr array byte) <- lookup *pixels-ah
    280     var pixels/edi: (addr array byte) <- copy _pixels
    281     compare pixels, 0
    282     break-if-=
    283     var y/ebx: int <- copy 0
    284     var height-addr/edx: (addr int) <- get target-screen, height
    285     var height/edx: int <- copy *height-addr
    286     height <- shift-left 4/log2-font-height
    287     {
    288       compare y, height
    289       break-if->=
    290       var width-addr/edx: (addr int) <- get target-screen, width
    291       var width/edx: int <- copy *width-addr
    292       width <- shift-left 3/log2-font-width
    293       var x/eax: int <- copy 0
    294       {
    295         compare x, width
    296         break-if->=
    297         {
    298           var idx/ecx: int <- pixel-index target-screen, x, y
    299           var color-addr/ecx: (addr byte) <- index pixels, idx
    300           var color/ecx: byte <- copy-byte *color-addr
    301           var color2/ecx: int <- copy color
    302           compare color2, 0
    303           break-if-=
    304           var x2/eax: int <- copy x
    305           x2 <- add left
    306           var y2/ebx: int <- copy y
    307           y2 <- add top
    308           pixel screen, x2, y2, color2
    309         }
    310         x <- increment
    311         loop
    312       }
    313       y <- increment
    314       loop
    315     }
    316   }
    317   return screen-y
    318 }
    319
    320 fn has-keyboard? _self: (addr sandbox) -> _/eax: boolean {
    321   var self/esi: (addr sandbox) <- copy _self
    322   var keyboard-obj-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
    323   var keyboard-obj-cell/eax: (addr cell) <- lookup *keyboard-obj-cell-ah
    324   compare keyboard-obj-cell, 0
    325   {
    326     break-if-!=
    327     return 0/false
    328   }
    329   var keyboard-obj-cell-type/ecx: (addr int) <- get keyboard-obj-cell, type
    330   compare *keyboard-obj-cell-type, 6/keyboard
    331   {
    332     break-if-=
    333     return 0/false
    334   }
    335   var keyboard-obj-ah/eax: (addr handle gap-buffer) <- get keyboard-obj-cell, keyboard-data
    336   var _keyboard-obj/eax: (addr gap-buffer) <- lookup *keyboard-obj-ah
    337   var keyboard-obj/edx: (addr gap-buffer) <- copy _keyboard-obj
    338   compare keyboard-obj, 0
    339   {
    340     break-if-!=
    341     return 0/false
    342   }
    343   return 1/true
    344 }
    345
    346 fn maybe-render-keyboard screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
    347   var self/esi: (addr sandbox) <- copy _self
    348   var keyboard-obj-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
    349   var keyboard-obj-cell/eax: (addr cell) <- lookup *keyboard-obj-cell-ah
    350   compare keyboard-obj-cell, 0
    351   {
    352     break-if-!=
    353     return ymin
    354   }
    355   var keyboard-obj-cell-type/ecx: (addr int) <- get keyboard-obj-cell, type
    356   compare *keyboard-obj-cell-type, 6/keyboard
    357   {
    358     break-if-=
    359     return ymin  # silently give up on rendering the keyboard
    360   }
    361   var keyboard-obj-ah/eax: (addr handle gap-buffer) <- get keyboard-obj-cell, keyboard-data
    362   var _keyboard-obj/eax: (addr gap-buffer) <- lookup *keyboard-obj-ah
    363   var keyboard-obj/edx: (addr gap-buffer) <- copy _keyboard-obj
    364   var y/ecx: int <- copy ymin
    365   y <- increment  # padding
    366   var x/eax: int <- draw-text-rightward screen, "keyboard: ", xmin, 0x99/xmax, y, 7/fg, 0xc5/bg=blue-bg
    367   var cursor-in-keyboard?/esi: (addr boolean) <- get self, cursor-in-keyboard?
    368   y <- render-keyboard screen, keyboard-obj, x, y, *cursor-in-keyboard?
    369   y <- increment  # padding
    370   return y
    371 }
    372
    373 fn render-keyboard screen: (addr screen), _keyboard: (addr gap-buffer), xmin: int, ymin: int, render-cursor?: boolean -> _/ecx: int {
    374   var keyboard/esi: (addr gap-buffer) <- copy _keyboard
    375   var width/edx: int <- copy 0x10/keyboard-capacity
    376   var y/edi: int <- copy ymin
    377   # keyboard
    378   var x/eax: int <- copy xmin
    379   var xmax/ecx: int <- copy x
    380   xmax <- add 0x10
    381   var ymax/edx: int <- copy ymin
    382   ymax <- add 1
    383   clear-rect screen, x, y, xmax, ymax, 0/bg
    384   x <- render-gap-buffer screen, keyboard, x, y, render-cursor?, 3/fg, 0/bg
    385   y <- increment
    386   return y
    387 }
    388
    389 fn print-screen-cell-of-fake-screen screen: (addr screen), _target: (addr screen), x: int, y: int {
    390   var target/ecx: (addr screen) <- copy _target
    391   var data-ah/eax: (addr handle array screen-cell) <- get target, data
    392   var data/eax: (addr array screen-cell) <- lookup *data-ah
    393   var index/ecx: int <- screen-cell-index target, x, y
    394   var offset/ecx: (offset screen-cell) <- compute-offset data, index
    395   var src-cell/esi: (addr screen-cell) <- index data, offset
    396   var src-grapheme/eax: (addr grapheme) <- get src-cell, data
    397   var src-color/ecx: (addr int) <- get src-cell, color
    398   var src-background-color/edx: (addr int) <- get src-cell, background-color
    399   draw-grapheme-at-cursor screen, *src-grapheme, *src-color, *src-background-color
    400 }
    401
    402 fn render-sandbox-menu screen: (addr screen), _self: (addr sandbox) {
    403   var _width/eax: int <- copy 0
    404   var height/ecx: int <- copy 0
    405   _width, height <- screen-size screen
    406   var width/edx: int <- copy _width
    407   var y/ecx: int <- copy height
    408   y <- decrement
    409   var height/ebx: int <- copy y
    410   height <- increment
    411   clear-rect screen, 0/x, y, width, height, 0xc5/bg=blue-bg=black
    412   set-cursor-position screen, 0/x, y
    413   draw-text-rightward-from-cursor screen, " ctrl+... ", width, 0xf/fg, 0xc5/bg=blue-bg
    414   draw-text-rightward-from-cursor screen, " r ", width, 0/fg, 0x5c/bg=black
    415   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0xc5/bg=blue-bg
    416   draw-text-rightward-from-cursor screen, " s ", width, 0/fg, 0x5c/bg=black
    417   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0xc5/bg=blue-bg
    418   $render-sandbox-menu:render-ctrl-m: {
    419     var self/eax: (addr sandbox) <- copy _self
    420     var has-trace?/eax: boolean <- has-trace? self
    421     compare has-trace?, 0/false
    422     {
    423       break-if-=
    424       draw-text-rightward-from-cursor screen, " m ", width, 0/fg, 0x38/bg=trace
    425       draw-text-rightward-from-cursor screen, " to trace  ", width, 7/fg, 0xc5/bg=blue-bg
    426       break $render-sandbox-menu:render-ctrl-m
    427     }
    428     draw-text-rightward-from-cursor screen, " m ", width, 0/fg, 3/bg=keyboard
    429     draw-text-rightward-from-cursor screen, " to keyboard  ", width, 7/fg, 0xc5/bg=blue-bg
    430   }
    431   draw-text-rightward-from-cursor screen, " a ", width, 0/fg, 0x5c/bg=black
    432   draw-text-rightward-from-cursor screen, " <<  ", width, 7/fg, 0xc5/bg=blue-bg
    433   draw-text-rightward-from-cursor screen, " b ", width, 0/fg, 0x5c/bg=black
    434   draw-text-rightward-from-cursor screen, " <word  ", width, 7/fg, 0xc5/bg=blue-bg
    435   draw-text-rightward-from-cursor screen, " f ", width, 0/fg, 0x5c/bg=black
    436   draw-text-rightward-from-cursor screen, " word>  ", width, 7/fg, 0xc5/bg=blue-bg
    437   draw-text-rightward-from-cursor screen, " e ", width, 0/fg, 0x5c/bg=black
    438   draw-text-rightward-from-cursor screen, " >>  ", width, 7/fg, 0xc5/bg=blue-bg
    439 }
    440
    441 fn render-keyboard-menu screen: (addr screen) {
    442   var width/eax: int <- copy 0
    443   var height/ecx: int <- copy 0
    444   width, height <- screen-size screen
    445   var y/ecx: int <- copy height
    446   y <- decrement
    447   var height/edx: int <- copy y
    448   height <- increment
    449   clear-rect screen, 0/x, y, width, height, 0xc5/bg=blue-bg=black
    450   set-cursor-position screen, 0/x, y
    451   draw-text-rightward-from-cursor screen, " ctrl+... ", width, 0xf/fg, 0xc5/bg=blue-bg
    452   draw-text-rightward-from-cursor screen, " r ", width, 0/fg, 0x5c/bg=black
    453   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0xc5/bg=blue-bg
    454   draw-text-rightward-from-cursor screen, " s ", width, 0/fg, 0x5c/bg=black
    455   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0xc5/bg=blue-bg
    456   draw-text-rightward-from-cursor screen, " m ", width, 0/fg, 7/bg
    457   draw-text-rightward-from-cursor screen, " to sandbox  ", width, 7/fg, 0xc5/bg=blue-bg
    458 }
    459
    460 fn edit-sandbox _self: (addr sandbox), key: byte, globals: (addr global-table), data-disk: (addr disk), real-screen: (addr screen), tweak-real-screen?: boolean {
    461   var self/esi: (addr sandbox) <- copy _self
    462   var g/edx: grapheme <- copy key
    463   # ctrl-s
    464   {
    465     compare g, 0x13/ctrl-s
    466     break-if-!=
    467     # minor gotcha here: any bindings created later in this iteration won't be
    468     # persisted until the next call to ctrl-s.
    469     store-state data-disk, self, globals
    470     # run sandbox
    471     var data-ah/ecx: (addr handle gap-buffer) <- get self, data
    472     var value-ah/eax: (addr handle stream byte) <- get self, value
    473     var _value/eax: (addr stream byte) <- lookup *value-ah
    474     var value/edx: (addr stream byte) <- copy _value
    475     var trace-ah/eax: (addr handle trace) <- get self, trace
    476     var _trace/eax: (addr trace) <- lookup *trace-ah
    477     var trace/ebx: (addr trace) <- copy _trace
    478     clear-trace trace
    479     {
    480       compare tweak-real-screen?, 0/false
    481       break-if-=
    482       clear-sandbox-output real-screen, self, 0x56/sandbox-left-margin, 1/y, 0x80/screen-width, 0x2f/screen-height-without-menu
    483     }
    484     var screen-cell/eax: (addr handle cell) <- get self, screen-var
    485     clear-screen-cell screen-cell
    486     var keyboard-cell/esi: (addr handle cell) <- get self, keyboard-var
    487     rewind-keyboard-cell keyboard-cell  # don't clear keys from before
    488     {
    489       compare tweak-real-screen?, 0/false
    490       break-if-=
    491       set-cursor-position real-screen, 0/x, 0/y  # for any debug prints during evaluation
    492     }
    493     run data-ah, value, globals, trace, screen-cell, keyboard-cell
    494     return
    495   }
    496   # ctrl-m
    497   {
    498     compare g, 0xd/ctrl-m
    499     break-if-!=
    500     # if cursor in data, switch to trace or fall through to keyboard
    501     {
    502       var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
    503       compare *cursor-in-data?, 0/false
    504       break-if-=
    505       var has-trace?/eax: boolean <- has-trace? self
    506       compare has-trace?, 0/false
    507       {
    508         break-if-=
    509         var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
    510         copy-to *cursor-in-data?, 0/false
    511         var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
    512         copy-to *cursor-in-trace?, 1/false
    513         return
    514       }
    515       var has-keyboard?/eax: boolean <- has-keyboard? self
    516       compare has-keyboard?, 0/false
    517       {
    518         break-if-=
    519         var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
    520         copy-to *cursor-in-data?, 0/false
    521         var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
    522         copy-to *cursor-in-keyboard?, 1/false
    523         return
    524       }
    525       return
    526     }
    527     # if cursor in trace, switch to keyboard or fall through to data
    528     {
    529       var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
    530       compare *cursor-in-trace?, 0/false
    531       break-if-=
    532       copy-to *cursor-in-trace?, 0/false
    533       var cursor-target/ecx: (addr boolean) <- get self, cursor-in-keyboard?
    534       var has-keyboard?/eax: boolean <- has-keyboard? self
    535       compare has-keyboard?, 0/false
    536       {
    537         break-if-!=
    538         cursor-target <- get self, cursor-in-data?
    539       }
    540       copy-to *cursor-target, 1/true
    541       return
    542     }
    543     # otherwise if cursor in keyboard, switch to data
    544     {
    545       var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
    546       compare *cursor-in-keyboard?, 0/false
    547       break-if-=
    548       copy-to *cursor-in-keyboard?, 0/false
    549       var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
    550       copy-to *cursor-in-data?, 1/true
    551       return
    552     }
    553     return
    554   }
    555   # if cursor in data, send key to data
    556   {
    557     var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
    558     compare *cursor-in-data?, 0/false
    559     break-if-=
    560     var data-ah/eax: (addr handle gap-buffer) <- get self, data
    561     var data/eax: (addr gap-buffer) <- lookup *data-ah
    562     edit-gap-buffer data, g
    563     return
    564   }
    565   # if cursor in keyboard, send key to keyboard
    566   {
    567     var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
    568     compare *cursor-in-keyboard?, 0/false
    569     break-if-=
    570     var keyboard-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
    571     var keyboard-cell/eax: (addr cell) <- lookup *keyboard-cell-ah
    572     compare keyboard-cell, 0
    573     {
    574       break-if-!=
    575       return
    576     }
    577     var keyboard-cell-type/ecx: (addr int) <- get keyboard-cell, type
    578     compare *keyboard-cell-type, 6/keyboard
    579     {
    580       break-if-=
    581       return
    582     }
    583     var keyboard-ah/eax: (addr handle gap-buffer) <- get keyboard-cell, keyboard-data
    584     var keyboard/eax: (addr gap-buffer) <- lookup *keyboard-ah
    585     edit-gap-buffer keyboard, g
    586     return
    587   }
    588   # if cursor in trace, send key to trace
    589   {
    590     var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
    591     compare *cursor-in-trace?, 0/false
    592     break-if-=
    593     var trace-ah/eax: (addr handle trace) <- get self, trace
    594     var trace/eax: (addr trace) <- lookup *trace-ah
    595     edit-trace trace, g
    596     return
    597   }
    598 }
    599
    600 fn run _in-ah: (addr handle gap-buffer), out: (addr stream byte), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell) {
    601   var in-ah/eax: (addr handle gap-buffer) <- copy _in-ah
    602   var in/eax: (addr gap-buffer) <- lookup *in-ah
    603   var read-result-h: (handle cell)
    604   var read-result-ah/esi: (addr handle cell) <- address read-result-h
    605   read-cell in, read-result-ah, trace
    606   var error?/eax: boolean <- has-errors? trace
    607   {
    608     compare error?, 0/false
    609     break-if-=
    610     return
    611   }
    612   var nil-storage: (handle cell)
    613   var nil-ah/eax: (addr handle cell) <- address nil-storage
    614   allocate-pair nil-ah
    615   var eval-result-storage: (handle cell)
    616   var eval-result/edi: (addr handle cell) <- address eval-result-storage
    617   debug-print "^", 4/fg, 0xc5/bg=blue-bg
    618   evaluate read-result-ah, eval-result, *nil-ah, globals, trace, screen-cell, keyboard-cell, 1/call-number
    619   debug-print "$", 4/fg, 0xc5/bg=blue-bg
    620   var error?/eax: boolean <- has-errors? trace
    621   {
    622     compare error?, 0/false
    623     break-if-=
    624     return
    625   }
    626   # if there was no error and the read-result starts with "set" or "def", save
    627   # the gap buffer in the modified global, then create a new one for the next
    628   # command.
    629   maybe-stash-gap-buffer-to-global globals, read-result-ah, _in-ah
    630   clear-stream out
    631   print-cell eval-result, out, trace
    632   mark-lines-dirty trace
    633 }
    634
    635 fn read-evaluate-and-move-to-globals _in-ah: (addr handle gap-buffer), globals: (addr global-table) {
    636   var in-ah/eax: (addr handle gap-buffer) <- copy _in-ah
    637   var in/eax: (addr gap-buffer) <- lookup *in-ah
    638   var read-result-h: (handle cell)
    639   var read-result-ah/esi: (addr handle cell) <- address read-result-h
    640   read-cell in, read-result-ah, 0/no-trace
    641   var nil-storage: (handle cell)
    642   var nil-ah/eax: (addr handle cell) <- address nil-storage
    643   allocate-pair nil-ah
    644   var eval-result-storage: (handle cell)
    645   var eval-result/edi: (addr handle cell) <- address eval-result-storage
    646   debug-print "^", 4/fg, 0xc5/bg=blue-bg
    647   evaluate read-result-ah, eval-result, *nil-ah, globals, 0/no-trace, 0/no-screen-cell, 0/no-keyboard-cell, 1/call-number
    648   debug-print "$", 4/fg, 0xc5/bg=blue-bg
    649   move-gap-buffer-to-global globals, read-result-ah, _in-ah
    650 }
    651
    652 fn test-run-integer {
    653   var sandbox-storage: sandbox
    654   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    655   initialize-sandbox-with sandbox, "1"
    656   # eval
    657   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    658   # setup: screen
    659   var screen-on-stack: screen
    660   var screen/edi: (addr screen) <- address screen-on-stack
    661   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    662   #
    663   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    664   # skip one line of padding
    665   check-screen-row screen, 1/y, " 1    ", "F - test-run-integer/0"
    666   check-screen-row screen, 2/y, " ...  ", "F - test-run-integer/1"
    667   check-screen-row screen, 3/y, " => 1 ", "F - test-run-integer/2"
    668 }
    669
    670 fn test-run-error-invalid-integer {
    671   var sandbox-storage: sandbox
    672   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    673   initialize-sandbox-with sandbox, "1a"
    674   # eval
    675   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    676   # setup: screen
    677   var screen-on-stack: screen
    678   var screen/edi: (addr screen) <- address screen-on-stack
    679   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    680   #
    681   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    682   # skip one line of padding
    683   check-screen-row screen, 1/y, " 1a             ", "F - test-run-error-invalid-integer/0"
    684   check-screen-row screen, 2/y, " ...            ", "F - test-run-error-invalid-integer/0"
    685   check-screen-row screen, 3/y, " invalid number ", "F - test-run-error-invalid-integer/2"
    686 }
    687
    688 fn test-run-with-spaces {
    689   var sandbox-storage: sandbox
    690   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    691   initialize-sandbox-with sandbox, " 1 \n"
    692   # eval
    693   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    694   # setup: screen
    695   var screen-on-stack: screen
    696   var screen/edi: (addr screen) <- address screen-on-stack
    697   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    698   #
    699   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    700   # skip one line of padding
    701   check-screen-row screen, 1/y, "  1   ", "F - test-run-with-spaces/0"
    702   check-screen-row screen, 2/y, "      ", "F - test-run-with-spaces/1"
    703   check-screen-row screen, 3/y, " ...  ", "F - test-run-with-spaces/2"
    704   check-screen-row screen, 4/y, " => 1 ", "F - test-run-with-spaces/3"
    705 }
    706
    707 fn test-run-quote {
    708   var sandbox-storage: sandbox
    709   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    710   initialize-sandbox-with sandbox, "'a"
    711   # eval
    712   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    713   # setup: screen
    714   var screen-on-stack: screen
    715   var screen/edi: (addr screen) <- address screen-on-stack
    716   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    717   #
    718   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    719   # skip one line of padding
    720   check-screen-row screen, 1/y, " 'a   ", "F - test-run-quote/0"
    721   check-screen-row screen, 2/y, " ...  ", "F - test-run-quote/1"
    722   check-screen-row screen, 3/y, " => a ", "F - test-run-quote/2"
    723 }
    724
    725 fn test-run-dotted-list {
    726   var sandbox-storage: sandbox
    727   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    728   initialize-sandbox-with sandbox, "'(a . b)"
    729   # eval
    730   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    731   # setup: screen
    732   var screen-on-stack: screen
    733   var screen/edi: (addr screen) <- address screen-on-stack
    734   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    735   #
    736   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    737   # skip one line of padding
    738   check-screen-row screen, 1/y, " '(a . b)   ", "F - test-run-dotted-list/0"
    739   check-screen-row screen, 2/y, " ...        ", "F - test-run-dotted-list/1"
    740   check-screen-row screen, 3/y, " => (a . b) ", "F - test-run-dotted-list/2"
    741 }
    742
    743 fn test-run-dot-and-list {
    744   var sandbox-storage: sandbox
    745   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    746   initialize-sandbox-with sandbox, "'(a . (b))"
    747   # eval
    748   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    749   # setup: screen
    750   var screen-on-stack: screen
    751   var screen/edi: (addr screen) <- address screen-on-stack
    752   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    753   #
    754   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    755   # skip one line of padding
    756   check-screen-row screen, 1/y, " '(a . (b)) ", "F - test-run-dot-and-list/0"
    757   check-screen-row screen, 2/y, " ...        ", "F - test-run-dot-and-list/1"
    758   check-screen-row screen, 3/y, " => (a b)   ", "F - test-run-dot-and-list/2"
    759 }
    760
    761 fn test-run-final-dot {
    762   var sandbox-storage: sandbox
    763   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    764   initialize-sandbox-with sandbox, "'(a .)"
    765   # eval
    766   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    767   # setup: screen
    768   var screen-on-stack: screen
    769   var screen/edi: (addr screen) <- address screen-on-stack
    770   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    771   #
    772   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    773   # skip one line of padding
    774   check-screen-row screen, 1/y, " '(a .)               ", "F - test-run-final-dot/0"
    775   check-screen-row screen, 2/y, " ...                  ", "F - test-run-final-dot/1"
    776   check-screen-row screen, 3/y, " '. )' makes no sense ", "F - test-run-final-dot/2"
    777   # further errors may occur
    778 }
    779
    780 fn test-run-double-dot {
    781   var sandbox-storage: sandbox
    782   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    783   initialize-sandbox-with sandbox, "'(a . .)"
    784   # eval
    785   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    786   # setup: screen
    787   var screen-on-stack: screen
    788   var screen/edi: (addr screen) <- address screen-on-stack
    789   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    790   #
    791   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    792   # skip one line of padding
    793   check-screen-row screen, 1/y, " '(a . .)             ", "F - test-run-double-dot/0"
    794   check-screen-row screen, 2/y, " ...                  ", "F - test-run-double-dot/1"
    795   check-screen-row screen, 3/y, " '. .' makes no sense ", "F - test-run-double-dot/2"
    796   # further errors may occur
    797 }
    798
    799 fn test-run-multiple-expressions-after-dot {
    800   var sandbox-storage: sandbox
    801   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    802   initialize-sandbox-with sandbox, "'(a . b c)"
    803   # eval
    804   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    805   # setup: screen
    806   var screen-on-stack: screen
    807   var screen/edi: (addr screen) <- address screen-on-stack
    808   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    809   #
    810   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    811   # skip one line of padding
    812   check-screen-row screen, 1/y, " '(a . b c)                                           ", "F - test-run-multiple-expressions-after-dot/0"
    813   check-screen-row screen, 2/y, " ...                                                  ", "F - test-run-multiple-expressions-after-dot/1"
    814   check-screen-row screen, 3/y, " cannot have multiple expressions between '.' and ')' ", "F - test-run-multiple-expressions-after-dot/2"
    815   # further errors may occur
    816 }
    817
    818 fn test-run-stream {
    819   var sandbox-storage: sandbox
    820   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    821   initialize-sandbox-with sandbox, "[a b]"
    822   # eval
    823   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    824   # setup: screen
    825   var screen-on-stack: screen
    826   var screen/edi: (addr screen) <- address screen-on-stack
    827   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    828   #
    829   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    830   # skip one line of padding
    831   check-screen-row screen, 1/y, " [a b]    ", "F - test-run-stream/0"
    832   check-screen-row screen, 2/y, " ...      ", "F - test-run-stream/1"
    833   check-screen-row screen, 3/y, " => [a b] ", "F - test-run-stream/2"
    834 }
    835
    836 fn test-run-move-cursor-into-trace {
    837   var sandbox-storage: sandbox
    838   var sandbox/esi: (addr sandbox) <- address sandbox-storage
    839   initialize-sandbox-with sandbox, "12"
    840   # eval
    841   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    842   # setup: screen
    843   var screen-on-stack: screen
    844   var screen/edi: (addr screen) <- address screen-on-stack
    845   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
    846   #
    847   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    848   # skip one line of padding
    849   check-screen-row screen,                                  1/y, " 12    ", "F - test-run-move-cursor-into-trace/pre-0"
    850   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "   |   ", "F - test-run-move-cursor-into-trace/pre-0/cursor"
    851   check-screen-row screen,                                  2/y, " ...   ", "F - test-run-move-cursor-into-trace/pre-1"
    852   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "       ", "F - test-run-move-cursor-into-trace/pre-1/cursor"
    853   check-screen-row screen,                                  3/y, " => 12 ", "F - test-run-move-cursor-into-trace/pre-2"
    854   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "       ", "F - test-run-move-cursor-into-trace/pre-2/cursor"
    855   # move cursor into trace
    856   edit-sandbox sandbox, 0xd/ctrl-m, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    857   #
    858   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    859   # skip one line of padding
    860   check-screen-row screen,                                  1/y, " 12    ", "F - test-run-move-cursor-into-trace/trace-0"
    861   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "       ", "F - test-run-move-cursor-into-trace/trace-0/cursor"
    862   check-screen-row screen,                                  2/y, " ...   ", "F - test-run-move-cursor-into-trace/trace-1"
    863   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " |||   ", "F - test-run-move-cursor-into-trace/trace-1/cursor"
    864   check-screen-row screen,                                  3/y, " => 12 ", "F - test-run-move-cursor-into-trace/trace-2"
    865   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "       ", "F - test-run-move-cursor-into-trace/trace-2/cursor"
    866   # move cursor into input
    867   edit-sandbox sandbox, 0xd/ctrl-m, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
    868   #
    869   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
    870   # skip one line of padding
    871   check-screen-row screen,                                  1/y, " 12    ", "F - test-run-move-cursor-into-trace/input-0"
    872   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "   |   ", "F - test-run-move-cursor-into-trace/input-0/cursor"
    873   check-screen-row screen,                                  2/y, " ...   ", "F - test-run-move-cursor-into-trace/input-1"
    874   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "       ", "F - test-run-move-cursor-into-trace/input-1/cursor"
    875   check-screen-row screen,                                  3/y, " => 12 ", "F - test-run-move-cursor-into-trace/input-2"
    876   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "       ", "F - test-run-move-cursor-into-trace/input-2/cursor"
    877 }
    878
    879 fn has-trace? _self: (addr sandbox) -> _/eax: boolean {
    880   var self/esi: (addr sandbox) <- copy _self
    881   var trace-ah/eax: (addr handle trace) <- get self, trace
    882   var _trace/eax: (addr trace) <- lookup *trace-ah
    883   var trace/edx: (addr trace) <- copy _trace
    884   compare trace, 0
    885   {
    886     break-if-!=
    887     return 0/false
    888   }
    889   var first-free/ebx: (addr int) <- get trace, first-free
    890   compare *first-free, 0
    891   {
    892     break-if->
    893     return 0/false
    894   }
    895   return 1/true
    896 }