https://github.com/akkartik/mu/blob/main/shell/sandbox.mu
  1 type sandbox {
  2   data: (handle gap-buffer)
  3   value: (handle stream byte)
  4   trace: (handle trace)
  5   cursor-in-trace?: boolean
  6 }
  7 
  8 fn initialize-sandbox _self: (addr sandbox) {
  9   var self/esi: (addr sandbox) <- copy _self
 10   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 11   allocate data-ah
 12   var data/eax: (addr gap-buffer) <- lookup *data-ah
 13   initialize-gap-buffer data, 0x1000/4KB
 14   var value-ah/eax: (addr handle stream byte) <- get self, value
 15   populate-stream value-ah, 0x1000/4KB
 16   var trace-ah/eax: (addr handle trace) <- get self, trace
 17   allocate trace-ah
 18   var trace/eax: (addr trace) <- lookup *trace-ah
 19   initialize-trace trace, 0x1000/lines, 0x80/visible-lines
 20 }
 21 
 22 ## some helpers for tests
 23 
 24 fn initialize-sandbox-with _self: (addr sandbox), s: (addr array byte) {
 25   var self/esi: (addr sandbox) <- copy _self
 26   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 27   allocate data-ah
 28   var data/eax: (addr gap-buffer) <- lookup *data-ah
 29   initialize-gap-buffer-with data, s
 30 }
 31 
 32 fn allocate-sandbox-with _out: (addr handle sandbox), s: (addr array byte) {
 33   var out/eax: (addr handle sandbox) <- copy _out
 34   allocate out
 35   var out-addr/eax: (addr sandbox) <- lookup *out
 36   initialize-sandbox-with out-addr, s
 37 }
 38 
 39 ##
 40 
 41 fn render-sandbox screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int {
 42   clear-screen screen
 43   var self/esi: (addr sandbox) <- copy _self
 44   # data
 45   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 46   var _data/eax: (addr gap-buffer) <- lookup *data-ah
 47   var data/edx: (addr gap-buffer) <- copy _data
 48   var x/eax: int <- copy xmin
 49   var y/ecx: int <- copy ymin
 50   var cursor-in-sandbox?/ebx: boolean <- copy 0/false
 51   {
 52     var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 53     compare *cursor-in-trace?, 0/false
 54     break-if-!=
 55     cursor-in-sandbox? <- copy 1/true
 56   }
 57   x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, cursor-in-sandbox?
 58   y <- increment
 59   # trace
 60   var trace-ah/eax: (addr handle trace) <- get self, trace
 61   var _trace/eax: (addr trace) <- lookup *trace-ah
 62   var trace/edx: (addr trace) <- copy _trace
 63   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 64   y <- render-trace screen, trace, xmin, y, xmax, ymax, *cursor-in-trace?
 65   # value
 66   $render-sandbox:value: {
 67     var value-ah/eax: (addr handle stream byte) <- get self, value
 68     var _value/eax: (addr stream byte) <- lookup *value-ah
 69     var value/esi: (addr stream byte) <- copy _value
 70     rewind-stream value
 71     var done?/eax: boolean <- stream-empty? value
 72     compare done?, 0/false
 73     break-if-!=
 74     var x/eax: int <- copy 0
 75     x, y <- draw-text-wrapping-right-then-down screen, "=> ", xmin, y, xmax, ymax, xmin, y, 7/fg, 0/bg
 76     var x2/edx: int <- copy x
 77     var dummy/eax: int <- draw-stream-rightward screen, value, x2, xmax, y, 7/fg=grey, 0/bg
 78   }
 79   # render menu
 80   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 81   compare *cursor-in-trace?, 0/false
 82   {
 83     break-if-=
 84     render-trace-menu screen
 85     return
 86   }
 87   render-sandbox-menu screen
 88 }
 89 
 90 fn render-sandbox-menu screen: (addr screen) {
 91   var width/eax: int <- copy 0
 92   var height/ecx: int <- copy 0
 93   width, height <- screen-size screen
 94   var y/ecx: int <- copy height
 95   y <- decrement
 96   set-cursor-position screen, 0/x, y
 97   draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey
 98   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0/bg
 99   draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 9/bg=blue
100   draw-text-rightward-from-cursor screen, " move to trace  ", width, 7/fg, 0/bg
101   draw-text-rightward-from-cursor screen, " ctrl-d ", width, 0/fg, 7/bg=grey
102   draw-text-rightward-from-cursor screen, " down  ", width, 7/fg, 0/bg
103   draw-text-rightward-from-cursor screen, " ctrl-u ", width, 0/fg, 7/bg=grey
104   draw-text-rightward-from-cursor screen, " up  ", width, 7/fg, 0/bg
105 }
106 
107 fn edit-sandbox _self: (addr sandbox), key: byte {
108   var self/esi: (addr sandbox) <- copy _self
109   var g/edx: grapheme <- copy key
110   # running code
111   {
112     compare g, 0x12/ctrl-r
113     break-if-!=
114     # ctrl-r: run function outside sandbox
115     # required: fn (addr screen), (addr keyboard)
116     # Mu will pass in the real screen and keyboard.
117     return
118   }
119   {
120     compare g, 0x13/ctrl-s
121     break-if-!=
122     # ctrl-s: run sandbox(es)
123     var data-ah/eax: (addr handle gap-buffer) <- get self, data
124     var _data/eax: (addr gap-buffer) <- lookup *data-ah
125     var data/ecx: (addr gap-buffer) <- copy _data
126     var value-ah/eax: (addr handle stream byte) <- get self, value
127     var _value/eax: (addr stream byte) <- lookup *value-ah
128     var value/edx: (addr stream byte) <- copy _value
129     var trace-ah/eax: (addr handle trace) <- get self, trace
130     var trace/eax: (addr trace) <- lookup *trace-ah
131     clear-trace trace
132     run data, value, trace
133     # testing write to disk
134 #?     rewind-stream value
135 #?     store-first-sector-to-primary-bus-secondary-drive value
136     return
137   }
138   # tab
139   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
140   {
141     compare g, 9/tab
142     break-if-!=
143     # if cursor in input, switch to trace
144     {
145       compare *cursor-in-trace?, 0/false
146       break-if-!=
147       copy-to *cursor-in-trace?, 1/true
148       return
149     }
150     # if cursor in trace, switch to input
151     copy-to *cursor-in-trace?, 0/false
152     return
153   }
154   # if cursor in trace, send cursor to trace
155   {
156     compare *cursor-in-trace?, 0/false
157     break-if-=
158     var trace-ah/eax: (addr handle trace) <- get self, trace
159     var trace/eax: (addr trace) <- lookup *trace-ah
160     edit-trace trace, g
161     return
162   }
163   # otherwise send cursor to input
164   var data-ah/eax: (addr handle gap-buffer) <- get self, data
165   var data/eax: (addr gap-buffer) <- lookup *data-ah
166   edit-gap-buffer data, g
167   return
168 }
169 
170 fn run in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) {
171   var read-result-storage: (handle cell)
172   var read-result/esi: (addr handle cell) <- address read-result-storage
173   read-cell in, read-result, trace
174   var error?/eax: boolean <- has-errors? trace
175   {
176     compare error?, 0/false
177     break-if-=
178     return
179   }
180   var nil-storage: (handle cell)
181   var nil-ah/eax: (addr handle cell) <- address nil-storage
182   allocate-pair nil-ah
183   var eval-result-storage: (handle cell)
184   var eval-result/edi: (addr handle cell) <- address eval-result-storage
185   evaluate read-result, eval-result, *nil-ah, trace
186   var error?/eax: boolean <- has-errors? trace
187   {
188     compare error?, 0/false
189     break-if-=
190     return
191   }
192   clear-stream out
193   print-cell eval-result, out, trace
194   mark-lines-dirty trace
195 }
196 
197 fn test-run-integer {
198   var sandbox-storage: sandbox
199   var sandbox/esi: (addr sandbox) <- address sandbox-storage
200   initialize-sandbox sandbox
201   # type "1"
202   edit-sandbox sandbox, 0x31/1
203   # eval
204   edit-sandbox sandbox, 0x13/ctrl-s
205   # setup: screen
206   var screen-on-stack: screen
207   var screen/edi: (addr screen) <- address screen-on-stack
208   initialize-screen screen, 0x80/width, 0x10/height
209   #
210   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
211   check-screen-row screen, 0/y, "1    ", "F - test-run-integer/0"
212   check-screen-row screen, 1/y, "...  ", "F - test-run-integer/1"
213   check-screen-row screen, 2/y, "=> 1 ", "F - test-run-integer/2"
214 }
215 
216 fn test-run-with-spaces {
217   var sandbox-storage: sandbox
218   var sandbox/esi: (addr sandbox) <- address sandbox-storage
219   initialize-sandbox sandbox
220   # type input with whitespace before and after
221   edit-sandbox sandbox, 0x20/space
222   edit-sandbox sandbox, 0x31/1
223   edit-sandbox sandbox, 0x20/space
224   edit-sandbox sandbox, 0xa/newline
225   # eval
226   edit-sandbox sandbox, 0x13/ctrl-s
227   # setup: screen
228   var screen-on-stack: screen
229   var screen/edi: (addr screen) <- address screen-on-stack
230   initialize-screen screen, 0x80/width, 0x10/height
231   #
232   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
233   check-screen-row screen, 0/y, " 1   ", "F - test-run-with-spaces/0"
234   check-screen-row screen, 1/y, "     ", "F - test-run-with-spaces/1"
235   check-screen-row screen, 2/y, "...  ", "F - test-run-with-spaces/2"
236   check-screen-row screen, 3/y, "=> 1 ", "F - test-run-with-spaces/3"
237 }
238 
239 fn test-run-error-invalid-integer {
240   var sandbox-storage: sandbox
241   var sandbox/esi: (addr sandbox) <- address sandbox-storage
242   initialize-sandbox sandbox
243   # type "1a"
244   edit-sandbox sandbox, 0x31/1
245   edit-sandbox sandbox, 0x61/a
246   # eval
247   edit-sandbox sandbox, 0x13/ctrl-s
248   # setup: screen
249   var screen-on-stack: screen
250   var screen/edi: (addr screen) <- address screen-on-stack
251   initialize-screen screen, 0x80/width, 0x10/height
252   #
253   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
254   check-screen-row screen, 0/y, "1a             ", "F - test-run-error-invalid-integer/0"
255   check-screen-row screen, 1/y, "...            ", "F - test-run-error-invalid-integer/0"
256   check-screen-row screen, 2/y, "invalid number ", "F - test-run-error-invalid-integer/2"
257 }
258 
259 fn test-run-move-cursor-into-trace {
260   var sandbox-storage: sandbox
261   var sandbox/esi: (addr sandbox) <- address sandbox-storage
262   initialize-sandbox sandbox
263   # type "12"
264   edit-sandbox sandbox, 0x31/1
265   edit-sandbox sandbox, 0x32/2
266   # eval
267   edit-sandbox sandbox, 0x13/ctrl-s
268   # setup: screen
269   var screen-on-stack: screen
270   var screen/edi: (addr screen) <- address screen-on-stack
271   initialize-screen screen, 0x80/width, 0x10/height
272   #
273   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
274   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/pre-0"
275   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  |   ", "F - test-run-move-cursor-into-trace/pre-0/cursor"
276   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/pre-1"
277   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-run-move-cursor-into-trace/pre-1/cursor"
278   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/pre-2"
279   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/pre-2/cursor"
280   # move cursor into trace
281   edit-sandbox sandbox, 9/tab
282   #
283   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
284   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/trace-0"
285   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-run-move-cursor-into-trace/trace-0/cursor"
286   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/trace-1"
287   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||   ", "F - test-run-move-cursor-into-trace/trace-1/cursor"
288   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/trace-2"
289   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/trace-2/cursor"
290   # move cursor into input
291   edit-sandbox sandbox, 9/tab
292   #
293   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
294   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/input-0"
295   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  |   ", "F - test-run-move-cursor-into-trace/input-0/cursor"
296   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/input-1"
297   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-run-move-cursor-into-trace/input-1/cursor"
298   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/input-2"
299   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/input-2/cursor"
300 }