# Mu - shell/environment.mu

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

      1 type environment {
      2   globals: global-table
      3   sandbox: sandbox
      4   partial-function-name: (handle gap-buffer)
      5   cursor-in-globals?: boolean
      6   cursor-in-function-modal?: boolean
      7 }
      8
      9 fn initialize-environment _self: (addr environment) {
     10   var self/esi: (addr environment) <- copy _self
     11   var globals/eax: (addr global-table) <- get self, globals
     12   initialize-globals globals
     13   var sandbox/eax: (addr sandbox) <- get self, sandbox
     14   initialize-sandbox sandbox, 1/with-screen
     15   var partial-function-name-ah/eax: (addr handle gap-buffer) <- get self, partial-function-name
     16   allocate partial-function-name-ah
     17   var partial-function-name/eax: (addr gap-buffer) <- lookup *partial-function-name-ah
     18   initialize-gap-buffer partial-function-name, 0x40/function-name-capacity
     19 }
     20
     21 fn render-environment screen: (addr screen), _self: (addr environment) {
     22   # globals layout: 1 char padding, 41 code, 1 padding, 41 code, 1 padding =  85
     23   # sandbox layout: 1 padding, 41 code, 1 padding                          =  43
     24   #                                                                  total = 128 chars
     25   var self/esi: (addr environment) <- copy _self
     26   var cursor-in-globals-a/eax: (addr boolean) <- get self, cursor-in-globals?
     27   var cursor-in-globals?/eax: boolean <- copy *cursor-in-globals-a
     28   var globals/ecx: (addr global-table) <- get self, globals
     29   render-globals screen, globals, cursor-in-globals?
     30   var sandbox/edx: (addr sandbox) <- get self, sandbox
     31   var cursor-in-sandbox?/ebx: boolean <- copy 1/true
     32   cursor-in-sandbox? <- subtract cursor-in-globals?
     33   render-sandbox screen, sandbox, 0x55/sandbox-left-margin, 0/sandbox-top-margin, 0x80/screen-width, 0x2f/screen-height-without-menu, cursor-in-sandbox?
     34   # modal if necessary
     35   {
     36     var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
     37     compare *cursor-in-function-modal-a, 0/false
     38     break-if-=
     39     render-function-modal screen, self
     40     render-function-modal-menu screen, self
     41     return
     42   }
     43   # render menu
     44   {
     45     var cursor-in-globals?/eax: (addr boolean) <- get self, cursor-in-globals?
     46     compare *cursor-in-globals?, 0/false
     47     break-if-=
     48     render-globals-menu screen, globals
     49     return
     50   }
     51   render-sandbox-menu screen, sandbox
     52 }
     53
     54 fn edit-environment _self: (addr environment), key: grapheme, data-disk: (addr disk) {
     55   var self/esi: (addr environment) <- copy _self
     56   var globals/edi: (addr global-table) <- get self, globals
     57   var sandbox/ecx: (addr sandbox) <- get self, sandbox
     58   # ctrl-r
     59   # Assumption: 'real-screen' and 'real-keyboard' are 0
     60   {
     61     compare key, 0x12/ctrl-r
     62     break-if-!=
     63     var tmp/eax: (addr handle cell) <- copy 0
     64     var nil: (handle cell)
     65     tmp <- address nil
     66     allocate-pair tmp
     67     # (main real-screen real-keyboard)
     68     var real-keyboard: (handle cell)
     69     tmp <- address real-keyboard
     70     allocate-keyboard tmp
     71     # args = cons(real-keyboard, nil)
     72     var args: (handle cell)
     73     tmp <- address args
     74     new-pair tmp, real-keyboard, nil
     75     #
     76     var real-screen: (handle cell)
     77     tmp <- address real-screen
     78     allocate-screen tmp
     79     #  args = cons(real-screen, args)
     80     tmp <- address args
     81     new-pair tmp, real-screen, *tmp
     82     #
     83     var main: (handle cell)
     84     tmp <- address main
     85     new-symbol tmp, "main"
     86     # args = cons(main, args)
     87     tmp <- address args
     88     new-pair tmp, main, *tmp
     89     # clear real screen
     90     clear-screen 0/screen
     91     set-cursor-position 0/screen, 0, 0
     92     # run
     93     var out: (handle cell)
     94     var out-ah/ecx: (addr handle cell) <- address out
     95     var trace-storage: trace
     96     var trace/ebx: (addr trace) <- address trace-storage
     97     initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
     98     evaluate tmp, out-ah, nil, globals, trace, 0/no-fake-screen, 0/no-fake-keyboard, 0/call-number
     99     # wait for a keypress
    100     {
    101       var tmp/eax: byte <- read-key 0/keyboard
    102       compare tmp, 0
    103       loop-if-=
    104     }
    105     #
    106     return
    107   }
    108   # ctrl-s: send multiple places
    109   {
    110     compare key, 0x13/ctrl-s
    111     break-if-!=
    112     {
    113       # cursor in function modal? do nothing
    114       var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    115       compare *cursor-in-function-modal-a, 0/false
    116       break-if-!=
    117       {
    118         # cursor in globals? update current definition
    119         var cursor-in-globals-a/edx: (addr boolean) <- get self, cursor-in-globals?
    120         compare *cursor-in-globals-a, 0/false
    121         break-if-=
    122         edit-globals globals, key
    123       }
    124       # update sandbox whether the cursor is in globals or sandbox
    125       edit-sandbox sandbox, key, globals, data-disk, 1/tweak-real-screen
    126     }
    127     return
    128   }
    129   # ctrl-g: go to a function (or the repl)
    130   {
    131     compare key, 7/ctrl-g
    132     break-if-!=
    133     var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    134     compare *cursor-in-function-modal-a, 0/false
    135     break-if-!=
    136     # look for a word to prepopulate the modal
    137     var current-word-storage: (stream byte 0x40)
    138     var current-word/edi: (addr stream byte) <- address current-word-storage
    139     word-at-cursor self, current-word
    140     var partial-function-name-ah/eax: (addr handle gap-buffer) <- get self, partial-function-name
    141     var partial-function-name/eax: (addr gap-buffer) <- lookup *partial-function-name-ah
    142     clear-gap-buffer partial-function-name
    143     load-gap-buffer-from-stream partial-function-name, current-word
    144     # enable the modal
    145     var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    146     copy-to *cursor-in-function-modal-a, 1/true
    147     return
    148   }
    149   # dispatch to function modal if necessary
    150   {
    151     var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    152     compare *cursor-in-function-modal-a, 0/false
    153     break-if-=
    154     # nested events for modal dialog
    155     # ignore spaces
    156     {
    157       compare key, 0x20/space
    158       break-if-!=
    159       return
    160     }
    161     # esc = exit modal dialog
    162     {
    163       compare key, 0x1b/escape
    164       break-if-!=
    165       var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    166       copy-to *cursor-in-function-modal-a, 0/false
    167       return
    168     }
    169     # enter = switch to function name and exit modal dialog
    170     {
    171       compare key, 0xa/newline
    172       break-if-!=
    173       var cursor-in-globals-a/edx: (addr boolean) <- get self, cursor-in-globals?
    174       copy-to *cursor-in-globals-a, 1/true
    175       var partial-function-name-ah/eax: (addr handle gap-buffer) <- get self, partial-function-name
    176       var partial-function-name/eax: (addr gap-buffer) <- lookup *partial-function-name-ah
    177       {
    178         {
    179           var empty?/eax: boolean <- gap-buffer-empty? partial-function-name
    180           compare empty?, 0/false
    181         }
    182         break-if-!=
    183         set-global-cursor-index globals, partial-function-name
    184       }
    185       var cursor-in-globals-a/ecx: (addr boolean) <- get self, cursor-in-globals?
    186       copy-to *cursor-in-globals-a, 1/true
    187       {
    188         var empty?/eax: boolean <- gap-buffer-empty? partial-function-name
    189         compare empty?, 0/false
    190         break-if-=
    191         copy-to *cursor-in-globals-a, 0/false
    192       }
    193       clear-gap-buffer partial-function-name
    194       var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    195       copy-to *cursor-in-function-modal-a, 0/false
    196       return
    197     }
    198     # otherwise process like a regular gap-buffer
    199     var partial-function-name-ah/eax: (addr handle gap-buffer) <- get self, partial-function-name
    200     var partial-function-name/eax: (addr gap-buffer) <- lookup *partial-function-name-ah
    201     edit-gap-buffer partial-function-name, key
    202     return
    203   }
    204   # dispatch the key to either sandbox or globals
    205   {
    206     var cursor-in-globals-a/eax: (addr boolean) <- get self, cursor-in-globals?
    207     compare *cursor-in-globals-a, 0/false
    208     break-if-=
    209     edit-globals globals, key
    210     return
    211   }
    212   edit-sandbox sandbox, key, globals, data-disk, 1/tweak-real-screen
    213 }
    214
    215 fn word-at-cursor _self: (addr environment), out: (addr stream byte) {
    216   var self/esi: (addr environment) <- copy _self
    217   var cursor-in-function-modal-a/eax: (addr boolean) <- get self, cursor-in-function-modal?
    218   compare *cursor-in-function-modal-a, 0/false
    219   {
    220     break-if-=
    221     # cursor in function modal
    222     return
    223   }
    224   var cursor-in-globals-a/edx: (addr boolean) <- get self, cursor-in-globals?
    225   compare *cursor-in-globals-a, 0/false
    226   {
    227     break-if-=
    228     # cursor in some function editor
    229     var globals/eax: (addr global-table) <- get self, globals
    230     var cursor-index-addr/ecx: (addr int) <- get globals, cursor-index
    231     var cursor-index/ecx: int <- copy *cursor-index-addr
    232     var globals-data-ah/eax: (addr handle array global) <- get globals, data
    233     var globals-data/eax: (addr array global) <- lookup *globals-data-ah
    234     var cursor-offset/ecx: (offset global) <- compute-offset globals-data, cursor-index
    235     var curr-global/eax: (addr global) <- index globals-data, cursor-offset
    236     var curr-global-data-ah/eax: (addr handle gap-buffer) <- get curr-global, input
    237     var curr-global-data/eax: (addr gap-buffer) <- lookup *curr-global-data-ah
    238     word-at-gap curr-global-data, out
    239     return
    240   }
    241   # cursor in sandbox
    242   var sandbox/ecx: (addr sandbox) <- get self, sandbox
    243   var sandbox-data-ah/eax: (addr handle gap-buffer) <- get sandbox, data
    244   var sandbox-data/eax: (addr gap-buffer) <- lookup *sandbox-data-ah
    245   word-at-gap sandbox-data, out
    246 }
    247
    248 fn render-function-modal screen: (addr screen), _self: (addr environment) {
    249   var self/esi: (addr environment) <- copy _self
    250   var width/eax: int <- copy 0
    251   var height/ecx: int <- copy 0
    252   width, height <- screen-size screen
    253   # xmin = max(0, width/2 - 0x20)
    254   var xmin: int
    255   var tmp/edx: int <- copy width
    256   tmp <- shift-right 1
    257   tmp <- subtract 0x20/half-function-name-capacity
    258   {
    259     compare tmp, 0
    260     break-if->=
    261     tmp <- copy 0
    262   }
    263   copy-to xmin, tmp
    264   # xmax = min(width, width/2 + 0x20)
    265   var xmax: int
    266   tmp <- copy width
    267   tmp <- shift-right 1
    268   tmp <- add 0x20/half-function-name-capacity
    269   {
    270     compare tmp, width
    271     break-if-<=
    272     tmp <- copy width
    273   }
    274   copy-to xmax, tmp
    275   # ymin = height/2 - 2
    276   var ymin: int
    277   tmp <- copy height
    278   tmp <- shift-right 1
    279   tmp <- subtract 2
    280   copy-to ymin, tmp
    281   # ymax = height/2 + 1
    282   var ymax: int
    283   tmp <- add 3
    284   copy-to ymax, tmp
    285   #
    286   clear-rect screen, xmin, ymin, xmax, ymax, 0xf/bg=modal
    287   add-to xmin, 4
    288   set-cursor-position screen, xmin, ymin
    289   draw-text-rightward-from-cursor screen, "go to function (or leave blank to go to REPL)", xmax, 8/fg=dark-grey, 0xf/bg=modal
    290   var partial-function-name-ah/eax: (addr handle gap-buffer) <- get self, partial-function-name
    291   var _partial-function-name/eax: (addr gap-buffer) <- lookup *partial-function-name-ah
    292   var partial-function-name/edx: (addr gap-buffer) <- copy _partial-function-name
    293   subtract-from xmin, 4
    294   add-to ymin 2
    295   var dummy/eax: int <- copy 0
    296   var dummy2/ecx: int <- copy 0
    297   dummy, dummy2 <- render-gap-buffer-wrapping-right-then-down screen, partial-function-name, xmin, ymin, xmax, ymax, 1/always-render-cursor, 0/fg=black, 0xf/bg=modal
    298 }
    299
    300 fn render-function-modal-menu screen: (addr screen), _self: (addr environment) {
    301   var self/esi: (addr environment) <- copy _self
    302   var _width/eax: int <- copy 0
    303   var height/ecx: int <- copy 0
    304   _width, height <- screen-size screen
    305   var width/edx: int <- copy _width
    306   var y/ecx: int <- copy height
    307   y <- decrement
    308   var height/ebx: int <- copy y
    309   height <- increment
    310   clear-rect screen, 0/x, y, width, height, 0xc5/bg=blue-bg
    311   set-cursor-position screen, 0/x, y
    312   draw-text-rightward-from-cursor screen, " ^r ", width, 0/fg, 0x5c/bg=menu-highlight
    313   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0xc5/bg=blue-bg
    314   draw-text-rightward-from-cursor screen, " enter ", width, 0/fg, 0x5c/bg=menu-highlight
    315   draw-text-rightward-from-cursor screen, " submit  ", width, 7/fg, 0xc5/bg=blue-bg
    316   draw-text-rightward-from-cursor screen, " esc ", width, 0/fg, 0x5c/bg=menu-highlight
    317   draw-text-rightward-from-cursor screen, " cancel  ", width, 7/fg, 0xc5/bg=blue-bg
    318   draw-text-rightward-from-cursor screen, " ^a ", width, 0/fg, 0x5c/bg=menu-highlight
    319   draw-text-rightward-from-cursor screen, " <<  ", width, 7/fg, 0xc5/bg=blue-bg
    320   draw-text-rightward-from-cursor screen, " ^b ", width, 0/fg, 0x5c/bg=menu-highlight
    321   draw-text-rightward-from-cursor screen, " <word  ", width, 7/fg, 0xc5/bg=blue-bg
    322   draw-text-rightward-from-cursor screen, " ^f ", width, 0/fg, 0x5c/bg=menu-highlight
    323   draw-text-rightward-from-cursor screen, " word>  ", width, 7/fg, 0xc5/bg=blue-bg
    324   draw-text-rightward-from-cursor screen, " ^e ", width, 0/fg, 0x5c/bg=menu-highlight
    325   draw-text-rightward-from-cursor screen, " >>  ", width, 7/fg, 0xc5/bg=blue-bg
    326 }
    327
    328 # Gotcha: some saved state may not load.
    329 fn load-state _self: (addr environment), data-disk: (addr disk) {
    330   var self/esi: (addr environment) <- copy _self
    331   # data-disk -> stream
    332   var s-storage: (stream byte 0x1000)  # space for 8/sectors
    333   var s/ebx: (addr stream byte) <- address s-storage
    334   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "loading sectors from data disk", 3/fg, 0/bg
    335   move-cursor-to-left-margin-of-next-line 0/screen
    336   load-sectors data-disk, 0/lba, 8/sectors, s
    337 #?   draw-stream-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg, 0xc5/bg=blue-bg
    338   # stream -> gap-buffer (HACK: we temporarily cannibalize the sandbox's gap-buffer)
    339   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "parsing", 3/fg, 0/bg
    340   move-cursor-to-left-margin-of-next-line 0/screen
    341   var sandbox/eax: (addr sandbox) <- get self, sandbox
    342   var data-ah/eax: (addr handle gap-buffer) <- get sandbox, data
    343   var data/eax: (addr gap-buffer) <- lookup *data-ah
    344   load-gap-buffer-from-stream data, s
    345   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "  into gap buffer", 3/fg, 0/bg
    346   move-cursor-to-left-margin-of-next-line 0/screen
    347   clear-stream s
    348   # read: gap-buffer -> cell
    349   var initial-root-storage: (handle cell)
    350   var initial-root/ecx: (addr handle cell) <- address initial-root-storage
    351   var trace-storage: trace
    352   var trace/edi: (addr trace) <- address trace-storage
    353   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
    354   read-cell data, initial-root, trace
    355   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "  into s-expressions", 3/fg, 0/bg
    356   move-cursor-to-left-margin-of-next-line 0/screen
    357   clear-gap-buffer data
    358   #
    359   {
    360     var initial-root-addr/eax: (addr cell) <- lookup *initial-root
    361     compare initial-root-addr, 0
    362     break-if-!=
    363     return
    364   }
    365   # load globals from assoc(initial-root, 'globals)
    366   var globals-literal-storage: (handle cell)
    367   var globals-literal-ah/eax: (addr handle cell) <- address globals-literal-storage
    368   new-symbol globals-literal-ah, "globals"
    369   var globals-literal/eax: (addr cell) <- lookup *globals-literal-ah
    370   var globals-cell-storage: (handle cell)
    371   var globals-cell-ah/edx: (addr handle cell) <- address globals-cell-storage
    372   clear-trace trace
    373   lookup-symbol globals-literal, globals-cell-ah, *initial-root, 0/no-globals, trace, 0/no-screen, 0/no-keyboard
    374   var globals-cell/eax: (addr cell) <- lookup *globals-cell-ah
    375   {
    376     compare globals-cell, 0
    377     break-if-=
    378     var globals/eax: (addr global-table) <- get self, globals
    379     load-globals globals-cell-ah, globals
    380   }
    381   # sandbox = assoc(initial-root, 'sandbox)
    382   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "loading sandbox", 3/fg, 0/bg
    383   var sandbox-literal-storage: (handle cell)
    384   var sandbox-literal-ah/eax: (addr handle cell) <- address sandbox-literal-storage
    385   new-symbol sandbox-literal-ah, "sandbox"
    386   var sandbox-literal/eax: (addr cell) <- lookup *sandbox-literal-ah
    387   var sandbox-cell-storage: (handle cell)
    388   var sandbox-cell-ah/edx: (addr handle cell) <- address sandbox-cell-storage
    389   clear-trace trace
    390   lookup-symbol sandbox-literal, sandbox-cell-ah, *initial-root, 0/no-globals, trace, 0/no-screen, 0/no-keyboard
    391   var sandbox-cell/eax: (addr cell) <- lookup *sandbox-cell-ah
    392   {
    393     compare sandbox-cell, 0
    394     break-if-=
    395     # print: cell -> stream
    396     clear-trace trace
    397     print-cell sandbox-cell-ah, s, trace
    398     # stream -> gap-buffer
    399     var sandbox/eax: (addr sandbox) <- get self, sandbox
    400     var data-ah/eax: (addr handle gap-buffer) <- get sandbox, data
    401     var data/eax: (addr gap-buffer) <- lookup *data-ah
    402     load-gap-buffer-from-stream data, s
    403   }
    404 }
    405
    406 # Save state as an alist of alists:
    407 #   ((globals . ((a . (fn ...))
    408 #                ...))
    409 #    (sandbox . ...))
    410 fn store-state data-disk: (addr disk), sandbox: (addr sandbox), globals: (addr global-table) {
    411   compare data-disk, 0/no-disk
    412   {
    413     break-if-!=
    414     return
    415   }
    416   var stream-storage: (stream byte 0x1000)  # space enough for 8/sectors
    417   var stream/edi: (addr stream byte) <- address stream-storage
    418   write stream, "(\n"
    419   write-globals stream, globals
    420   write-sandbox stream, sandbox
    421   write stream, ")\n"
    422   store-sectors data-disk, 0/lba, 8/sectors, stream
    423 }