https://github.com/akkartik/mu/blob/main/shell/global.mu
  1 type global-table {
  2   data: (handle array global)
  3   final-index: int
  4   render-list: (handle array int)  # sequence of globals to render on the left side
  5                                    # no pagination or scrolling
  6 }
  7 
  8 type global {
  9   name: (handle array byte)
 10   input: (handle gap-buffer)
 11   value: (handle cell)
 12   trace: (handle trace)
 13 }
 14 
 15 fn initialize-globals _self: (addr global-table) {
 16   var self/esi: (addr global-table) <- copy _self
 17   compare self, 0
 18   {
 19     break-if-!=
 20     abort "initialize globals"
 21     return
 22   }
 23   var data-ah/eax: (addr handle array global) <- get self, data
 24   populate data-ah, 0x80
 25   initialize-primitives self
 26   var render-ah/eax: (addr handle array int) <- get self, render-list
 27   populate render-ah, 0x20/render-size
 28 }
 29 
 30 fn load-globals in: (addr handle cell), self: (addr global-table) {
 31   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "loading globals:", 3/fg, 0/bg
 32   var remaining-ah/esi: (addr handle cell) <- copy in
 33   {
 34     var _remaining/eax: (addr cell) <- lookup *remaining-ah
 35     var remaining/ebx: (addr cell) <- copy _remaining
 36     var done?/eax: boolean <- nil? remaining
 37     compare done?, 0/false
 38     break-if-!=
 39     var curr-ah/eax: (addr handle cell) <- get remaining, left
 40     var _curr/eax: (addr cell) <- lookup *curr-ah
 41     var curr/ecx: (addr cell) <- copy _curr
 42     remaining-ah <- get remaining, right
 43     var name-ah/eax: (addr handle cell) <- get curr, left
 44     var name/eax: (addr cell) <- lookup *name-ah
 45     var name-data-ah/eax: (addr handle stream byte) <- get name, text-data
 46     var _name-data/eax: (addr stream byte) <- lookup *name-data-ah
 47     var name-data/edx: (addr stream byte) <- copy _name-data
 48     rewind-stream name-data
 49     draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 3/fg 0/bg
 50     draw-stream-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, name-data, 3/fg, 0/bg
 51     var value-ah/eax: (addr handle cell) <- get curr, right
 52     var value/eax: (addr cell) <- lookup *value-ah
 53     var value-data-ah/eax: (addr handle stream byte) <- get value, text-data
 54     var _value-data/eax: (addr stream byte) <- lookup *value-data-ah
 55     var value-data/ecx: (addr stream byte) <- copy _value-data
 56     var value-gap-buffer-storage: (handle gap-buffer)
 57     var value-gap-buffer-ah/edi: (addr handle gap-buffer) <- address value-gap-buffer-storage
 58     allocate value-gap-buffer-ah
 59     var value-gap-buffer/eax: (addr gap-buffer) <- lookup *value-gap-buffer-ah
 60     initialize-gap-buffer value-gap-buffer, 0x40000/256KB
 61     load-gap-buffer-from-stream value-gap-buffer, value-data
 62     load-lexical-scope value-gap-buffer-ah, self
 63     loop
 64   }
 65   move-cursor-to-left-margin-of-next-line 0/screen
 66 }
 67 
 68 fn write-globals out: (addr stream byte), _self: (addr global-table) {
 69   var self/esi: (addr global-table) <- copy _self
 70   compare self, 0
 71   {
 72     break-if-!=
 73     abort "write globals"
 74     return
 75   }
 76   write out, "  (globals . (\n"
 77   var data-ah/eax: (addr handle array global) <- get self, data
 78   var data/eax: (addr array global) <- lookup *data-ah
 79   var final-index/edx: (addr int) <- get self, final-index
 80   var curr-index/ecx: int <- copy 1/skip-0
 81   {
 82     compare curr-index, *final-index
 83     break-if->
 84     var curr-offset/ebx: (offset global) <- compute-offset data, curr-index
 85     var curr/ebx: (addr global) <- index data, curr-offset
 86     var curr-value-ah/edx: (addr handle cell) <- get curr, value
 87     var curr-value/eax: (addr cell) <- lookup *curr-value-ah
 88     var curr-type/eax: (addr int) <- get curr-value, type
 89     {
 90       compare *curr-type, 4/primitive-function
 91       break-if-=
 92       compare *curr-type, 5/screen
 93       break-if-=
 94       compare *curr-type, 6/keyboard
 95       break-if-=
 96       write out, "    ("
 97       var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 98       var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
 99       write out, curr-name
100       write out, " . ["
101       var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
102       var curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
103       {
104         compare curr-input, 0
105         break-if-!=
106         abort "null gap buffer"
107       }
108       append-gap-buffer curr-input, out
109       write out, "])\n"
110     }
111     curr-index <- increment
112     loop
113   }
114   write out, "  ))\n"
115 }
116 
117 # globals layout: 1 char padding, 41 code, 1 padding, 41 code, 1 padding = 85 chars
118 fn render-globals screen: (addr screen), _self: (addr global-table), show-cursor?: boolean {
119   clear-rect screen, 0/xmin, 0/ymin, 0x55/xmax, 0x2f/ymax=screen-height-without-menu, 0xdc/bg=green-bg
120   var self/esi: (addr global-table) <- copy _self
121   compare self, 0
122   {
123     break-if-!=
124     abort "render globals"
125     return
126   }
127   var data-ah/eax: (addr handle array global) <- get self, data
128   var _data/eax: (addr array global) <- lookup *data-ah
129   var data/ebx: (addr array global) <- copy _data
130   var curr-index/edx: int <- copy 0
131   var y1: int
132   copy-to y1, 1/padding-top
133   var y2: int
134   copy-to y2, 1/padding-top
135   $render-globals:loop: {
136     compare curr-index, 0x20/render-size
137     break-if->=
138     {
139       compare y1, 0x2f/ymax
140       break-if-<
141       compare y2, 0x2f/ymax
142       break-if-<
143       break $render-globals:loop
144     }
145     $render-globals:iter: {
146       var cursor-in-current-line?: boolean
147       {
148         compare show-cursor?, 0/false
149         break-if-=
150         compare curr-index, 0
151         break-if-!=
152         copy-to cursor-in-current-line?, 1/true
153       }
154       var render-list-ah/eax: (addr handle array int) <- get self, render-list
155       var render-list/eax: (addr array int) <- lookup *render-list-ah
156       var global-id-a/eax: (addr int) <- index render-list, curr-index
157       var global-id/eax: int <- copy *global-id-a
158       compare global-id, 0
159       break-if-=
160       var global-offset/edx: (offset global) <- compute-offset data, global-id
161       var curr/edx: (addr global) <- index data, global-offset
162       {
163         var render-image?/eax: boolean <- render-image? curr, cursor-in-current-line?
164         compare render-image?, 0/false
165         break-if-=
166         var y/ecx: int <- render-image-definition screen, curr, 0/x y1
167         y <- increment  # padding
168         copy-to y1, y
169         break $render-globals:iter
170       }
171       var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
172       var _curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
173       var curr-input/ebx: (addr gap-buffer) <- copy _curr-input
174       compare curr-input, 0
175       break-if-=
176       var curr-trace-ah/eax: (addr handle trace) <- get curr, trace
177       var _curr-trace/eax: (addr trace) <- lookup *curr-trace-ah
178       var curr-trace/edx: (addr trace) <- copy _curr-trace
179       $render-globals:render-global: {
180         var x/eax: int <- copy 0
181         var y/ecx: int <- copy y1
182         compare y, y2
183         {
184           break-if->=
185           x, y <- render-gap-buffer-wrapping-right-then-down screen, curr-input, 1/padding-left, y1, 0x2a/xmax, 0x2f/ymax, cursor-in-current-line?, 7/fg=definition, 0xc5/bg=blue-bg
186           y <- increment
187           y <- render-trace screen, curr-trace, 1/padding-left, y, 0x2a/xmax, 0x2f/ymax, 0/no-cursor
188           y <- increment
189           copy-to y1, y
190           break $render-globals:render-global
191         }
192         x, y <- render-gap-buffer-wrapping-right-then-down screen, curr-input, 0x2b/xmin, y2, 0x54/xmax, 0x2f/ymax, cursor-in-current-line?, 7/fg=definition, 0xc5/bg=blue-bg
193         y <- increment
194         y <- render-trace screen, curr-trace, 0x2b/xmin, y, 0x54/xmax, 0x2f/ymax, 0/no-cursor
195         y <- increment
196         copy-to y2, y
197       }
198     }
199     curr-index <- increment
200     loop
201   }
202   # render primitives on top
203   render-primitives screen, 1/xmin=padding-left, 0x55/xmax, 0x2f/ymax
204 }
205 
206 fn render-globals-menu screen: (addr screen), _self: (addr global-table) {
207   var _width/eax: int <- copy 0
208   var height/ecx: int <- copy 0
209   _width, height <- screen-size screen
210   var width/edx: int <- copy _width
211   var y/ecx: int <- copy height
212   y <- decrement
213   var height/ebx: int <- copy y
214   height <- increment
215   clear-rect screen, 0/x, y, width, height, 0xc5/bg=blue-bg
216   set-cursor-position screen, 0/x, y
217   draw-text-rightward-from-cursor screen, " ^r ", width, 0/fg, 0x5c/bg=menu-highlight
218   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0xc5/bg=blue-bg
219   draw-text-rightward-from-cursor screen, " ^s ", width, 0/fg, 0x5c/bg=menu-highlight
220   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0xc5/bg=blue-bg
221   draw-text-rightward-from-cursor screen, " ^g ", width, 0/fg, 0x5c/bg=menu-highlight
222   draw-text-rightward-from-cursor screen, " go to  ", width, 7/fg, 0xc5/bg=blue-bg
223   draw-text-rightward-from-cursor screen, " ^a ", width, 0/fg, 0x5c/bg=menu-highlight
224   draw-text-rightward-from-cursor screen, " <<  ", width, 7/fg, 0xc5/bg=blue-bg
225   draw-text-rightward-from-cursor screen, " ^b ", width, 0/fg, 0x5c/bg=menu-highlight
226   draw-text-rightward-from-cursor screen, " <word  ", width, 7/fg, 0xc5/bg=blue-bg
227   draw-text-rightward-from-cursor screen, " ^f ", width, 0/fg, 0x5c/bg=menu-highlight
228   draw-text-rightward-from-cursor screen, " word>  ", width, 7/fg, 0xc5/bg=blue-bg
229   draw-text-rightward-from-cursor screen, " ^e ", width, 0/fg, 0x5c/bg=menu-highlight
230   draw-text-rightward-from-cursor screen, " >>  ", width, 7/fg, 0xc5/bg=blue-bg
231 }
232 
233 fn edit-globals _self: (addr global-table), key: code-point-utf8 {
234   var self/esi: (addr global-table) <- copy _self
235   # ctrl-s
236   {
237     compare key, 0x13/ctrl-s
238     break-if-!=
239     #
240     refresh-cursor-definition self
241     return
242   }
243   var cursor-index/ecx: int <- cursor-global self
244   compare cursor-index, 0
245   {
246     break-if-!=
247     return
248   }
249   var data-ah/eax: (addr handle array global) <- get self, data
250   var data/eax: (addr array global) <- lookup *data-ah
251   var cursor-offset/ecx: (offset global) <- compute-offset data, cursor-index
252   var curr-global/eax: (addr global) <- index data, cursor-offset
253   var curr-editor-ah/eax: (addr handle gap-buffer) <- get curr-global, input
254   var curr-editor/eax: (addr gap-buffer) <- lookup *curr-editor-ah
255   edit-gap-buffer curr-editor, key
256 }
257 
258 fn create-empty-global _self: (addr global-table), name-stream: (addr stream byte), capacity: int {
259   var self/esi: (addr global-table) <- copy _self
260   var final-index-addr/ecx: (addr int) <- get self, final-index
261   increment *final-index-addr
262   var render-list-ah/eax: (addr handle array int) <- get self, render-list
263   var render-list/eax: (addr array int) <- lookup *render-list-ah
264   slide-down render-list, 0/start 0x1f/penultimate, 1/target
265   var curr-index/ecx: int <- copy *final-index-addr
266   var dest/eax: (addr int) <- index render-list, 0
267   copy-to *dest, curr-index
268   var data-ah/eax: (addr handle array global) <- get self, data
269   var data/eax: (addr array global) <- lookup *data-ah
270   var curr-offset/ecx: (offset global) <- compute-offset data, curr-index
271   var curr/esi: (addr global) <- index data, curr-offset
272   var curr-name-ah/eax: (addr handle array byte) <- get curr, name
273   stream-to-array name-stream, curr-name-ah
274   var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
275   allocate curr-input-ah
276   var curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
277   initialize-gap-buffer curr-input, capacity
278   var trace-ah/eax: (addr handle trace) <- get curr, trace
279   allocate trace-ah
280   var trace/eax: (addr trace) <- lookup *trace-ah
281   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
282 }
283 
284 fn refresh-cursor-definition _self: (addr global-table) {
285   var self/esi: (addr global-table) <- copy _self
286   var render-list-ah/eax: (addr handle array int) <- get self, render-list
287   var render-list/eax: (addr array int) <- lookup *render-list-ah
288   var cursor-index/edx: (addr int) <- index render-list, 0
289   refresh-definition self, *cursor-index
290 }
291 
292 fn refresh-definition _self: (addr global-table), _index: int {
293   var self/esi: (addr global-table) <- copy _self
294   var data-ah/eax: (addr handle array global) <- get self, data
295   var data/eax: (addr array global) <- lookup *data-ah
296   var index/ebx: int <- copy _index
297   var offset/ebx: (offset global) <- compute-offset data, index
298   var curr-global/ebx: (addr global) <- index data, offset
299   var curr-input-ah/edx: (addr handle gap-buffer) <- get curr-global, input
300   var curr-trace-ah/eax: (addr handle trace) <- get curr-global, trace
301   var curr-trace/eax: (addr trace) <- lookup *curr-trace-ah
302   clear-trace curr-trace
303   var curr-value-ah/edi: (addr handle cell) <- get curr-global, value
304   var definitions-created-storage: (stream int 0x10)
305   var definitions-created/ecx: (addr stream int) <- address definitions-created-storage
306   read-and-evaluate-and-save-gap-buffer-to-globals curr-input-ah, curr-value-ah, self, definitions-created, curr-trace, 0/no-screen, 0/no-keyboard
307 }
308 
309 fn assign-or-create-global _self: (addr global-table), name: (addr array byte), value: (handle cell), index-updated: (addr int), trace: (addr trace) {
310   var self/esi: (addr global-table) <- copy _self
311   compare self, 0
312   {
313     break-if-!=
314     abort "assign global"
315   }
316   var curr-index/ecx: int <- find-symbol-name-in-globals self, name
317   {
318     compare curr-index, -1/not-found
319     break-if-!=
320     var final-index-addr/eax: (addr int) <- get self, final-index
321     increment *final-index-addr
322     curr-index <- copy *final-index-addr
323     var arr-ah/eax: (addr handle array int) <- get self, render-list
324     var arr/eax: (addr array int) <- lookup *arr-ah
325     slide-down arr, 0/start 0x1e/penultimate, 1/target
326     var dest/eax: (addr int) <- index arr, 0
327     copy-to *dest, curr-index
328   }
329   var data-ah/eax: (addr handle array global) <- get self, data
330   var data/eax: (addr array global) <- lookup *data-ah
331   var curr-offset/esi: (offset global) <- compute-offset data, curr-index
332   var curr/esi: (addr global) <- index data, curr-offset
333   var curr-name-ah/eax: (addr handle array byte) <- get curr, name
334   copy-array-object name, curr-name-ah
335   var curr-value-ah/eax: (addr handle cell) <- get curr, value
336   copy-handle value, curr-value-ah
337   var index-updated/edi: (addr int) <- copy index-updated
338   copy-to *index-updated, curr-index
339   var trace-ah/eax: (addr handle trace) <- get curr, trace
340   allocate trace-ah
341   var trace/eax: (addr trace) <- lookup *trace-ah
342   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
343 }
344 
345 fn bump-global _globals: (addr global-table), global-id: int {
346   var globals/esi: (addr global-table) <- copy _globals
347   var render-list-ah/eax: (addr handle array int) <- get globals, render-list
348   var render-list/eax: (addr array int) <- lookup *render-list-ah
349   var idx/ecx: int <- find-slide-down-slot-in-array render-list, global-id
350   {
351     compare idx, 0
352     break-if-!=
353     return
354   }
355   slide-down render-list, 0/start idx, 1/target
356   var dest/eax: (addr int) <- index render-list, 0
357   var val/ecx: int <- copy global-id
358   copy-to *dest, val
359 }
360 
361 fn cursor-global _globals: (addr global-table) -> _/ecx: int {
362   var globals/esi: (addr global-table) <- copy _globals
363   var render-list-ah/eax: (addr handle array int) <- get globals, render-list
364   var render-list/eax: (addr array int) <- lookup *render-list-ah
365   var dest/eax: (addr int) <- index render-list, 0
366   return *dest
367 }
368 
369 fn lookup-symbol-in-globals _sym: (addr cell), out: (addr handle cell), _globals: (addr global-table), trace: (addr trace), inner-screen-var: (addr handle cell), inner-keyboard-var: (addr handle cell) {
370   var sym/eax: (addr cell) <- copy _sym
371   var sym-name-ah/eax: (addr handle stream byte) <- get sym, text-data
372   var _sym-name/eax: (addr stream byte) <- lookup *sym-name-ah
373   var sym-name/edx: (addr stream byte) <- copy _sym-name
374   var globals/esi: (addr global-table) <- copy _globals
375   {
376     compare globals, 0
377     break-if-=
378     var curr-index/ecx: int <- find-symbol-in-globals globals, sym-name
379     compare curr-index, -1/not-found
380     break-if-=
381     var global-data-ah/eax: (addr handle array global) <- get globals, data
382     var global-data/eax: (addr array global) <- lookup *global-data-ah
383     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
384     var curr/ebx: (addr global) <- index global-data, curr-offset
385     var curr-value/eax: (addr handle cell) <- get curr, value
386     copy-object curr-value, out
387     return
388   }
389   # if sym is "screen" and inner-screen-var exists, return it
390   {
391     var sym-is-screen?/eax: boolean <- stream-data-equal? sym-name, "screen"
392     compare sym-is-screen?, 0/false
393     break-if-=
394     compare inner-screen-var, 0
395     break-if-=
396     copy-object inner-screen-var, out
397     return
398   }
399   # if sym is "keyboard" and inner-keyboard-var exists, return it
400   {
401     var sym-is-keyboard?/eax: boolean <- stream-data-equal? sym-name, "keyboard"
402     compare sym-is-keyboard?, 0/false
403     break-if-=
404     compare inner-keyboard-var, 0
405     break-if-=
406     copy-object inner-keyboard-var, out
407     return
408   }
409   # otherwise error "unbound symbol: ", sym
410   var stream-storage: (stream byte 0x40)
411   var stream/ecx: (addr stream byte) <- address stream-storage
412   write stream, "unbound symbol: "
413   rewind-stream sym-name
414   write-stream stream, sym-name
415   error-stream trace, stream
416 }
417 
418 fn maybe-lookup-symbol-in-globals _sym: (addr cell), out: (addr handle cell), _globals: (addr global-table), trace: (addr trace) {
419   var sym/eax: (addr cell) <- copy _sym
420   var sym-name-ah/eax: (addr handle stream byte) <- get sym, text-data
421   var _sym-name/eax: (addr stream byte) <- lookup *sym-name-ah
422   var sym-name/edx: (addr stream byte) <- copy _sym-name
423   var globals/esi: (addr global-table) <- copy _globals
424   {
425     compare globals, 0
426     break-if-=
427     var curr-index/ecx: int <- find-symbol-in-globals globals, sym-name
428     compare curr-index, -1/not-found
429     break-if-=
430     var global-data-ah/eax: (addr handle array global) <- get globals, data
431     var global-data/eax: (addr array global) <- lookup *global-data-ah
432     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
433     var curr/ebx: (addr global) <- index global-data, curr-offset
434     var curr-value/eax: (addr handle cell) <- get curr, value
435     copy-object curr-value, out
436     return
437   }
438 }
439 
440 # return the index in globals containing 'sym'
441 # or -1 if not found
442 fn find-symbol-in-globals _globals: (addr global-table), sym-name: (addr stream byte) -> _/ecx: int {
443   var globals/esi: (addr global-table) <- copy _globals
444   compare globals, 0
445   {
446     break-if-!=
447     return -1/not-found
448   }
449   var global-data-ah/eax: (addr handle array global) <- get globals, data
450   var global-data/eax: (addr array global) <- lookup *global-data-ah
451   var final-index/ecx: (addr int) <- get globals, final-index
452   var curr-index/ecx: int <- copy *final-index
453   {
454     compare curr-index, 0
455     break-if-<
456     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
457     var curr/ebx: (addr global) <- index global-data, curr-offset
458     var curr-name-ah/eax: (addr handle array byte) <- get curr, name
459     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
460     var found?/eax: boolean <- stream-data-equal? sym-name, curr-name
461     compare found?, 0/false
462     {
463       break-if-=
464       return curr-index
465     }
466     curr-index <- decrement
467     loop
468   }
469   return -1/not-found
470 }
471 
472 # return the index in globals containing 'sym'
473 # or -1 if not found
474 fn find-symbol-name-in-globals _globals: (addr global-table), sym-name: (addr array byte) -> _/ecx: int {
475   var globals/esi: (addr global-table) <- copy _globals
476   compare globals, 0
477   {
478     break-if-!=
479     return -1/not-found
480   }
481   var global-data-ah/eax: (addr handle array global) <- get globals, data
482   var global-data/eax: (addr array global) <- lookup *global-data-ah
483   var final-index/ecx: (addr int) <- get globals, final-index
484   var curr-index/ecx: int <- copy *final-index
485   {
486     compare curr-index, 0
487     break-if-<
488     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
489     var curr/ebx: (addr global) <- index global-data, curr-offset
490     var curr-name-ah/eax: (addr handle array byte) <- get curr, name
491     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
492     var found?/eax: boolean <- string-equal? sym-name, curr-name
493     compare found?, 0/false
494     {
495       break-if-=
496       return curr-index
497     }
498     curr-index <- decrement
499     loop
500   }
501   return -1/not-found
502 }
503 
504 fn mutate-binding-in-globals name: (addr stream byte), val: (addr handle cell), _globals: (addr global-table), trace: (addr trace) {
505   var globals/esi: (addr global-table) <- copy _globals
506   {
507     compare globals, 0
508     break-if-=
509     var curr-index/ecx: int <- find-symbol-in-globals globals, name
510     compare curr-index, -1/not-found
511     break-if-=
512     var global-data-ah/eax: (addr handle array global) <- get globals, data
513     var global-data/eax: (addr array global) <- lookup *global-data-ah
514     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
515     var curr/ebx: (addr global) <- index global-data, curr-offset
516     var dest/eax: (addr handle cell) <- get curr, value
517     copy-object val, dest
518     return
519   }
520   # otherwise error "unbound symbol: ", sym
521   var stream-storage: (stream byte 0x40)
522   var stream/ecx: (addr stream byte) <- address stream-storage
523   write stream, "unbound symbol: "
524   rewind-stream name
525   write-stream stream, name
526   error-stream trace, stream
527 }
528 
529 fn stash-gap-buffer-to-globals _globals: (addr global-table), definitions: (addr stream int), gap: (addr handle gap-buffer) {
530   var globals/eax: (addr global-table) <- copy _globals
531   compare globals, 0
532   {
533     break-if-!=
534     return
535   }
536   var global-data-ah/eax: (addr handle array global) <- get globals, data
537   var global-data/eax: (addr array global) <- lookup *global-data-ah
538   rewind-stream definitions
539   {
540     {
541       var done?/eax: boolean <- stream-empty? definitions
542       compare done?, 0/false
543     }
544     break-if-!=
545     var index: int
546     var index-addr/ecx: (addr int) <- address index
547     read-from-stream definitions, index-addr
548     var index/ecx: int <- copy *index-addr
549     var offset/ebx: (offset global) <- compute-offset global-data, index
550     var dest-global/eax: (addr global) <- index global-data, offset
551     var dest-ah/eax: (addr handle gap-buffer) <- get dest-global, input
552     copy-object gap, dest-ah
553     loop
554   }
555 }
556 
557 # load all bindings in a single lexical scope, aka gap buffer of the environment, aka file of the file system
558 fn load-lexical-scope in-ah: (addr handle gap-buffer), _globals: (addr global-table) {
559   var globals/esi: (addr global-table) <- copy _globals
560   var definitions-created-storage: (stream int 0x10)
561   var definitions-created/ebx: (addr stream int) <- address definitions-created-storage
562   var trace-h: (handle trace)
563   var trace-ah/edx: (addr handle trace) <- address trace-h
564   allocate trace-ah
565   var trace/eax: (addr trace) <- lookup *trace-ah
566   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
567   var dummy-result-h: (handle cell)
568   var dummy-result-ah/ecx: (addr handle cell) <- address dummy-result-h
569   read-and-evaluate-and-save-gap-buffer-to-globals in-ah, dummy-result-ah, globals, definitions-created, trace, 0/no-inner-screen-var, 0/no-inner-keyboard-var
570   #
571   # save trace to all needed globals as well
572   rewind-stream definitions-created
573   var globals-data-ah/eax: (addr handle array global) <- get globals, data
574   var _globals-data/eax: (addr array global) <- lookup *globals-data-ah
575   var globals-data/edi: (addr array global) <- copy _globals-data
576   {
577     var no-definitions?/eax: boolean <- stream-empty? definitions-created
578     compare no-definitions?, 0/false
579     break-if-!=
580     var curr-index: int
581     var curr-index-a/eax: (addr int) <- address curr-index
582     read-from-stream definitions-created, curr-index-a
583     var curr-offset/eax: (offset global) <- compute-offset globals-data, curr-index
584     var curr-global/ecx: (addr global) <- index globals-data, curr-offset
585     var curr-trace-ah/eax: (addr handle trace) <- get curr-global, trace
586     copy-object trace-ah, curr-trace-ah
587     loop
588   }
589 }
590 
591 fn render-image? _self: (addr global), cursor-in-definition?: boolean -> _/eax: boolean {
592   {
593     compare cursor-in-definition?, 0/false
594     break-if-=
595     # if the cursor is in this definition we need to be able to edit raw data
596     return 0/false
597   }
598   var self/esi: (addr global) <- copy _self
599   var value-ah/eax: (addr handle cell) <- get self, value
600   var value/eax: (addr cell) <- lookup *value-ah
601   compare value, 0
602   {
603     break-if-!=
604     # unparsed buffers can't be rendered; either they're uninitialized or they have errors
605     return 0/false
606   }
607   {
608     var pair?/eax: boolean <- pair? value
609     compare pair?, 0/false
610     break-if-!=
611     # not a pair? not an image
612     return 0/false
613   }
614   var first-ah/eax: (addr handle cell) <- get value, left
615   var first/eax: (addr cell) <- lookup *first-ah
616   var litimg?/eax: boolean <- litimg? first
617   return pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * parser.c
 *
 * Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
 *
 * This file is part of Profanity.
 *
 * Profanity is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Profanity is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdlib.h>
#include <string.h>

#include <glib.h>

/*
 * Take a full line of input and return an array of strings representing
 * the arguments of a command.
 * If the number of arguments found is less than min, or more than max
 * NULL is returned.
 *
 * inp - The