https://github.com/akkartik/mu/blob/main/linux/tile/environment.mu
   1 # The architecture that seems to be crystallizing: the environment has two
   2 # areas: functions and sandbox.
   3 #
   4 # Rendering the environment requires rendering all areas.
   5 # Displaying the cursor requires displaying cursor for the area controlling the cursor.
   6 # Processing events for the environment requires processing events for the area controlling the cursor.
   7 #
   8 # Areas can have dialogs.
   9 # There can also be global dialogs (currently just one: goto function).
  10 # Areas are responsible for rendering their dialogs.
  11 # In practice this results in dialogs encapsulating the state they need to
  12 # decide whether to render.
  13 #
  14 # This will be useful if we add more areas in the future.
  15 
  16 type environment {
  17   screen: (handle screen)
  18   functions: (handle function)
  19   sandboxes: (handle sandbox)
  20   partial-function-name: (handle word)
  21   # at most one of these will be set
  22   cursor-function: (handle function)
  23   cursor-sandbox: (handle sandbox)
  24   #
  25   nrows: int
  26   ncols: int
  27   code-separator-col: int
  28 }
  29 
  30 fn initialize-environment _env: (addr environment) {
  31   var env/esi: (addr environment) <- copy _env
  32   # initialize some predefined function definitions
  33   var functions/eax: (addr handle function) <- get env, functions
  34   create-primitive-functions functions
  35   # initialize first sandbox
  36   var sandbox-ah/ecx: (addr handle sandbox) <- get env, sandboxes
  37   allocate sandbox-ah
  38   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
  39   initialize-sandbox sandbox
  40   # initialize cursor sandbox
  41   var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox
  42   copy-object sandbox-ah, cursor-sandbox-ah
  43   # initialize screen
  44   var screen-ah/eax: (addr handle screen) <- get env, screen
  45   var _screen/eax: (addr screen) <- lookup *screen-ah
  46   var screen/edi: (addr screen) <- copy _screen
  47   var nrows/eax: int <- copy 0
  48   var ncols/ecx: int <- copy 0
  49   nrows, ncols <- screen-size screen
  50   var dest/edx: (addr int) <- get env, nrows
  51   copy-to *dest, nrows
  52   dest <- get env, ncols
  53   copy-to *dest, ncols
  54   var repl-col/ecx: int <- copy ncols
  55   repl-col <- shift-right 1
  56   dest <- get env, code-separator-col
  57   copy-to *dest, repl-col
  58 }
  59 
  60 fn initialize-environment-with-fake-screen _self: (addr environment), nrows: int, ncols: int {
  61   var self/esi: (addr environment) <- copy _self
  62   var screen-ah/eax: (addr handle screen) <- get self, screen
  63   allocate screen-ah
  64   var screen-addr/eax: (addr screen) <- lookup *screen-ah
  65   initialize-screen screen-addr, nrows, ncols
  66   initialize-environment self
  67 }
  68 
  69 #############
  70 # Iterate
  71 #############
  72 
  73 fn process _self: (addr environment), key: grapheme {
  74   var self/esi: (addr environment) <- copy _self
  75   var fn-name-ah/eax: (addr handle word) <- get self, partial-function-name
  76   var fn-name/eax: (addr word) <- lookup *fn-name-ah
  77   compare fn-name, 0
  78   {
  79     break-if-=
  80 #?     print-string 0, "processing goto fn\n"
  81     process-goto-dialog self, key
  82     return
  83   }
  84   var function-ah/eax: (addr handle function) <- get self, cursor-function
  85   var function/eax: (addr function) <- lookup *function-ah
  86   compare function, 0
  87   {
  88     break-if-=
  89 #?     print-string 0, "processing function\n"
  90     process-function self, function, key
  91     return
  92   }
  93   var sandbox-ah/eax: (addr handle sandbox) <- get self, cursor-sandbox
  94   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
  95   compare sandbox, 0
  96   {
  97     break-if-=
  98 #?     print-string 0, "processing sandbox\n"
  99     process-sandbox self, sandbox, key
 100     return
 101   }
 102 }
 103 
 104 # collect new name in partial-function-name, and move the cursor to function with that name
 105 fn process-goto-dialog _self: (addr environment), key: grapheme {
 106   var self/esi: (addr environment) <- copy _self
 107   var fn-name-ah/edi: (addr handle word) <- get self, partial-function-name
 108   # if 'esc' pressed, cancel goto
 109   compare key, 0x1b/esc
 110   $process-goto-dialog:cancel: {
 111     break-if-!=
 112     clear-object fn-name-ah
 113     return
 114   }
 115   # if 'enter' pressed, location function and set cursor to it
 116   compare key, 0xa/enter
 117   $process-goto-dialog:commit: {
 118     break-if-!=
 119 #?     print-string 0, "jump\n"
 120     var fn-name/eax: (addr word) <- lookup *fn-name-ah
 121     var functions/ecx: (addr handle function) <- get self, functions
 122     var dest/edx: (addr handle function) <- get self, cursor-function
 123     callee functions, fn-name, dest
 124     # we won't clear cursor-sandbox until we start supporting multiple sandboxes
 125     clear-object fn-name-ah
 126     # there shouldn't be any need to clear state for other dialogs in the sandbox
 127     return
 128   }
 129   #
 130   compare key, 0x7f/del  # backspace on Macs
 131   $process-goto-dialog:backspace: {
 132     break-if-!=
 133     # if not at start, delete grapheme before cursor
 134     var fn-name/eax: (addr word) <- lookup *fn-name-ah
 135     var at-start?/eax: boolean <- cursor-at-start? fn-name
 136     compare at-start?, 0/false
 137     {
 138       break-if-!=
 139       var fn-name/eax: (addr word) <- lookup *fn-name-ah
 140       delete-before-cursor fn-name
 141     }
 142     return
 143   }
 144   # otherwise insert key within current word
 145   var print?/eax: boolean <- real-grapheme? key
 146   $process-goto-dialog:real-grapheme: {
 147     compare print?, 0/false
 148     break-if-=
 149     var fn-name/eax: (addr word) <- lookup *fn-name-ah
 150     add-grapheme-to-word fn-name, key
 151     return
 152   }
 153   # silently ignore other hotkeys
 154 }
 155 
 156 fn process-function _self: (addr environment), _function: (addr function), key: grapheme {
 157   var self/esi: (addr environment) <- copy _self
 158   var function/edi: (addr function) <- copy _function
 159   process-function-edit self, function, key
 160 }
 161 
 162 fn process-function-edit _self: (addr environment), _function: (addr function), key: grapheme {
 163   var self/esi: (addr environment) <- copy _self
 164   var function/edi: (addr function) <- copy _function
 165   var cursor-word-ah/ebx: (addr handle word) <- get function, cursor-word
 166   var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
 167   var cursor-word/ecx: (addr word) <- copy _cursor-word
 168   compare key, 0x445b1b/left-arrow
 169   $process-function-edit:key-left-arrow: {
 170     break-if-!=
 171 #?     print-string 0, "left-arrow\n"
 172     # if not at start, move left within current word
 173     var at-start?/eax: boolean <- cursor-at-start? cursor-word
 174     compare at-start?, 0/false
 175     {
 176       break-if-!=
 177 #?       print-string 0, "cursor left within word\n"
 178       cursor-left cursor-word
 179       return
 180     }
 181     # otherwise move to end of previous word
 182     var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
 183     var prev-word/eax: (addr word) <- lookup *prev-word-ah
 184     {
 185       compare prev-word, 0
 186       break-if-=
 187 #?       print-string 0, "move to previous word\n"
 188       cursor-to-end prev-word
 189       copy-object prev-word-ah, cursor-word-ah
 190     }
 191     return
 192   }
 193   compare key, 0x435b1b/right-arrow
 194   $process-function-edit:key-right-arrow: {
 195     break-if-!=
 196     # if not at end, move right within current word
 197     var at-end?/eax: boolean <- cursor-at-end? cursor-word
 198     compare at-end?, 0/false
 199     {
 200       break-if-!=
 201       cursor-right cursor-word
 202       return
 203     }
 204     # otherwise, move to the next word
 205     var next-word-ah/edx: (addr handle word) <- get cursor-word, next
 206     var next-word/eax: (addr word) <- lookup *next-word-ah
 207     {
 208       compare next-word, 0
 209       break-if-=
 210       cursor-to-start next-word
 211       copy-object next-word-ah, cursor-word-ah
 212     }
 213     return
 214   }
 215   # word-based motions
 216   compare key, 2/ctrl-b
 217   $process-function-edit:prev-word: {
 218     break-if-!=
 219     # jump to previous word if possible
 220     var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
 221     var prev-word/eax: (addr word) <- lookup *prev-word-ah
 222     {
 223       compare prev-word, 0
 224       break-if-=
 225       cursor-to-end prev-word
 226       copy-object prev-word-ah, cursor-word-ah
 227     }
 228     return
 229   }
 230   compare key, 6/ctrl-f
 231   $process-function-edit:next-word: {
 232     break-if-!=
 233     # jump to previous word if possible
 234     var next-word-ah/edx: (addr handle word) <- get cursor-word, next
 235     var next-word/eax: (addr word) <- lookup *next-word-ah
 236     {
 237       compare next-word, 0
 238       break-if-=
 239       cursor-to-end next-word
 240       copy-object next-word-ah, cursor-word-ah
 241     }
 242     return
 243   }
 244   # line-based motions
 245   compare key, 1/ctrl-a
 246   $process-function-edit:start-of-line: {
 247     break-if-!=
 248     # move cursor to start of first word
 249     var body-ah/eax: (addr handle line) <- get function, body
 250     var body/eax: (addr line) <- lookup *body-ah
 251     var body-contents-ah/ecx: (addr handle word) <- get body, data
 252     copy-object body-contents-ah, cursor-word-ah
 253     var body-contents/eax: (addr word) <- lookup *body-contents-ah
 254     cursor-to-start body-contents
 255     return
 256   }
 257   compare key, 5/ctrl-e
 258   $process-function-edit:end-of-line: {
 259     break-if-!=
 260     # move cursor to end of final word
 261     var body-ah/eax: (addr handle line) <- get function, body
 262     var body/eax: (addr line) <- lookup *body-ah
 263     var body-contents-ah/ecx: (addr handle word) <- get body, data
 264     copy-object body-contents-ah, cursor-word-ah
 265     final-word cursor-word-ah, cursor-word-ah
 266     var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
 267     cursor-to-end cursor-word
 268     return
 269   }
 270   # bounce to another function
 271   compare key, 7/ctrl-g
 272   $process-function-edit:goto-function: {
 273     break-if-!=
 274     # initialize dialog to name function to jump to
 275     var partial-function-name-ah/eax: (addr handle word) <- get self, partial-function-name
 276     allocate partial-function-name-ah
 277     var partial-function-name/eax: (addr word) <- lookup *partial-function-name-ah
 278     initialize-word partial-function-name
 279     return
 280   }
 281   # bounce to sandbox
 282   compare key, 9/tab
 283   $process-function-edit:goto-sandbox: {
 284     break-if-!=
 285     var function-ah/eax: (addr handle function) <- get self, cursor-function
 286     clear-object function-ah
 287     return
 288   }
 289   # editing the current function
 290   compare key, 0x7f/del  # backspace on Macs
 291   $process-function-edit:backspace: {
 292     break-if-!=
 293     # if not at start of some word, delete grapheme before cursor within current word
 294     var at-start?/eax: boolean <- cursor-at-start? cursor-word
 295     compare at-start?, 0/false
 296     {
 297       break-if-!=
 298       delete-before-cursor cursor-word
 299       return
 300     }
 301     # otherwise delete current word and move to end of prev word
 302     var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
 303     var prev-word/eax: (addr word) <- lookup *prev-word-ah
 304     {
 305       compare prev-word, 0
 306       break-if-=
 307       cursor-to-end prev-word
 308       delete-next prev-word
 309       copy-object prev-word-ah, cursor-word-ah
 310     }
 311     return
 312   }
 313   compare key, 0x20/space
 314   $process-function-edit:space: {
 315     break-if-!=
 316 #?     print-string 0, "space\n"
 317     # if cursor is at start of word, insert word before
 318     {
 319       var at-start?/eax: boolean <- cursor-at-start? cursor-word
 320       compare at-start?, 0/false
 321       break-if-=
 322       var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev
 323       append-word prev-word-ah
 324       var new-prev-word-ah/eax: (addr handle word) <- get cursor-word, prev
 325       copy-object new-prev-word-ah, cursor-word-ah
 326       return
 327     }
 328     # if start of word is quote and grapheme before cursor is not, just insert it as usual
 329     # TODO: support string escaping
 330     {
 331       var first-grapheme/eax: grapheme <- first-grapheme cursor-word
 332       compare first-grapheme, 0x22/double-quote
 333       break-if-!=
 334       var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word
 335       compare final-grapheme, 0x22/double-quote
 336       break-if-=
 337       break $process-function-edit:space
 338     }
 339     # if start of word is '[' and grapheme before cursor is not ']', just insert it as usual
 340     # TODO: support nested arrays
 341     {
 342       var first-grapheme/eax: grapheme <- first-grapheme cursor-word
 343       compare first-grapheme, 0x5b/[
 344       break-if-!=
 345       var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word
 346       compare final-grapheme, 0x5d/]
 347       break-if-=
 348       break $process-function-edit:space
 349     }
 350     # otherwise insert word after and move cursor to it for the next key
 351     # (but we'll continue to track the current cursor-word for the rest of this function)
 352     append-word cursor-word-ah
 353     var next-word-ah/eax: (addr handle word) <- get cursor-word, next
 354     copy-object next-word-ah, cursor-word-ah
 355     # if cursor is at end of word, that's all
 356     var at-end?/eax: boolean <- cursor-at-end? cursor-word
 357     compare at-end?, 0/false
 358     {
 359       break-if-=
 360       return
 361     }
 362     # otherwise we're in the middle of a word
 363     # move everything after cursor to the (just created) next word
 364     var next-word-ah/eax: (addr handle word) <- get cursor-word, next
 365     var _next-word/eax: (addr word) <- lookup *next-word-ah
 366     var next-word/ebx: (addr word) <- copy _next-word
 367     {
 368       var at-end?/eax: boolean <- cursor-at-end? cursor-word
 369       compare at-end?, 0/false
 370       break-if-!=
 371       var g/eax: grapheme <- pop-after-cursor cursor-word
 372       add-grapheme-to-word next-word, g
 373       loop
 374     }
 375     cursor-to-start next-word
 376     return
 377   }
 378   # otherwise insert key within current word
 379   var g/edx: grapheme <- copy key
 380   var print?/eax: boolean <- real-grapheme? key
 381   $process-function-edit:real-grapheme: {
 382     compare print?, 0/false
 383     break-if-=
 384     add-grapheme-to-word cursor-word, g
 385     return
 386   }
 387   # silently ignore other hotkeys
 388 }
 389 
 390 fn process-sandbox _self: (addr environment), _sandbox: (addr sandbox), key: grapheme {
 391   var self/esi: (addr environment) <- copy _self
 392   var sandbox/edi: (addr sandbox) <- copy _sandbox
 393   var rename-word-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-cursor-word
 394   var rename-word-mode?/eax: (addr word) <- lookup *rename-word-mode-ah?
 395   compare rename-word-mode?, 0
 396   {
 397     break-if-=
 398 #?     print-string 0, "processing sandbox rename\n"
 399     process-sandbox-rename sandbox, key
 400     return
 401   }
 402   var define-function-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-function
 403   var define-function-mode?/eax: (addr word) <- lookup *define-function-mode-ah?
 404   compare define-function-mode?, 0
 405   {
 406     break-if-=
 407 #?     print-string 0, "processing function definition\n"
 408     var functions/ecx: (addr handle function) <- get self, functions
 409     process-sandbox-define sandbox, functions, key
 410     return
 411   }
 412 #?   print-string 0, "processing sandbox edit\n"
 413   process-sandbox-edit self, sandbox, key
 414 }
 415 
 416 fn process-sandbox-edit _self: (addr environment), _sandbox: (addr sandbox), key: grapheme {
 417   var self/esi: (addr environment) <- copy _self
 418   var sandbox/edi: (addr sandbox) <- copy _sandbox
 419   var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
 420   var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
 421   var cursor-word-ah/ebx: (addr handle word) <- get cursor-call-path, word
 422   var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
 423   var cursor-word/ecx: (addr word) <- copy _cursor-word
 424   compare key, 0x445b1b/left-arrow
 425   $process-sandbox-edit:key-left-arrow: {
 426     break-if-!=
 427 #?     print-string 0, "left-arrow\n"
 428     # if not at start, move left within current word
 429     var at-start?/eax: boolean <- cursor-at-start? cursor-word
 430     compare at-start?, 0/false
 431     {
 432       break-if-!=
 433 #?       print-string 0, "cursor left within word\n"
 434       cursor-left cursor-word
 435       return
 436     }
 437     # if current word is expanded, move to the rightmost word in its body
 438     {
 439       var cursor-call-path/esi: (addr handle call-path-element) <- get sandbox, cursor-call-path
 440       var expanded-words/edx: (addr handle call-path) <- get sandbox, expanded-words
 441       var curr-word-is-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path
 442       compare curr-word-is-expanded?, 0/false
 443       break-if-=
 444       # update cursor-call-path
 445 #?       print-string 0, "curr word is expanded\n"
 446       var self/ecx: (addr environment) <- copy _self
 447       var functions/ecx: (addr handle function) <- get self, functions
 448       var body: (handle line)
 449       var body-ah/eax: (addr handle line) <- address body
 450       function-body functions, cursor-word-ah, body-ah
 451       var body-addr/eax: (addr line) <- lookup *body-ah
 452       var first-word-ah/edx: (addr handle word) <- get body-addr, data
 453       var final-word-h: (handle word)
 454       var final-word-ah/eax: (addr handle word) <- address final-word-h
 455       final-word first-word-ah, final-word-ah
 456       push-to-call-path-element cursor-call-path, final-word-ah
 457       # move cursor to end of word
 458       var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
 459       var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
 460       var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
 461       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
 462       cursor-to-end cursor-word
 463       return
 464     }
 465     # if at first word, look for a caller to jump to
 466     $process-sandbox-edit:key-left-arrow-first-word: {
 467       var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
 468       var prev-word/eax: (addr word) <- lookup *prev-word-ah
 469       compare prev-word, 0
 470       break-if-!=
 471       $process-sandbox-edit:key-left-arrow-first-word-and-caller: {
 472 #?         print-string 0, "return\n"
 473         {
 474           var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path
 475           var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
 476           var next-cursor-element-ah/edx: (addr handle call-path-element) <- get cursor-call-path, next
 477           var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah
 478           compare next-cursor-element, 0
 479           break-if-= $process-sandbox-edit:key-left-arrow-first-word-and-caller
 480           copy-object next-cursor-element-ah, cursor-call-path-ah
 481         }
 482         var cursor-call-path-ah/eax: (addr handle call-path-element) <- get 
<strong>QUOTES_REGEXP</strong> = &lt;_sre.SRE_Pattern object&gt;<br>
<strong>SPECIAL_CHARS_REGEXP</strong> = &lt;_sre.SRE_Pattern object&gt;<br>
<strong>TITLE_REGEXP</strong> = &lt;_sre.SRE_Pattern object&gt;</td></tr></table>
</body></html>
gEdx">edx: (addr handle word) <- get cursor-word, prev 491 var prev-word/eax: (addr word) <- lookup *prev-word-ah 492 { 493 compare prev-word, 0 494 break-if-= 495 #? print-string 0, "move to previous word\n" 496 cursor-to-end prev-word 497 #? { 498 #? var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 499 #? var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 500 #? var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 501 #? var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah 502 #? var cursor-word/ebx: (addr word) <- copy _cursor-word 503 #? print-string 0, "word at cursor before: " 504 #? print-word 0, cursor-word 505 #? print-string 0, "\n" 506 #? } 507 var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 508 decrement-final-element cursor-call-path 509 #? { 510 #? var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 511 #? var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 512 #? var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 513 #? var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah 514 #? var cursor-word/ebx: (addr word) <- copy _cursor-word 515 #? print-string 0, "word at cursor after: " 516 #? print-word 0, cursor-word 517 #? print-string 0, "\n" 518 #? } 519 } 520 return 521 } 522 compare key, 0x435b1b/right-arrow 523 $process-sandbox-edit:key-right-arrow: { 524 break-if-!= 525 # if not at end, move right within current word 526 var at-end?/eax: boolean <- cursor-at-end? cursor-word 527 compare at-end?, 0/false 528 { 529 break-if-!= 530 #? print-string 0, "a\n" 531 cursor-right cursor-word 532 return 533 } 534 # if at final word, look for a caller to jump to 535 { 536 var next-word-ah/edx: (addr handle word) <- get cursor-word, next 537 var next-word/eax: (addr word) <- lookup *next-word-ah 538 compare next-word, 0 539 break-if-!= 540 var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path 541 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 542 var next-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next 543 var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah 544 compare next-cursor-element, 0 545 break-if-= 546 copy-object next-cursor-element-ah, cursor-call-path-ah 547 return 548 } 549 # otherwise, move to the next word 550 var next-word-ah/edx: (addr handle word) <- get cursor-word, next 551 var next-word/eax: (addr word) <- lookup *next-word-ah 552 { 553 compare next-word, 0 554 break-if-= 555 #? print-string 0, "b\n" 556 cursor-to-start next-word 557 # . . cursor-word now out of date 558 var cursor-call-path/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path 559 increment-final-element cursor-call-path 560 # Is the new cursor word expanded? If so, it's a function call. Add a 561 # new level to the cursor-call-path for the call's body. 562 $process-sandbox-edit:key-right-arrow-next-word-is-call-expanded: { 563 #? print-string 0, "c\n" 564 { 565 var expanded-words/eax: (addr handle call-path) <- get sandbox, expanded-words 566 var curr-word-is-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path 567 compare curr-word-is-expanded?, 0/false 568 break-if-= $process-sandbox-edit:key-right-arrow-next-word-is-call-expanded 569 } 570 var callee-h: (handle function) 571 var callee-ah/edx: (addr handle function) <- address callee-h 572 var functions/ebx: (addr handle function) <- get self, functions 573 callee functions, next-word, callee-ah 574 var callee/eax: (addr function) <- lookup *callee-ah 575 var callee-body-ah/eax: (addr handle line) <- get callee, body 576 var callee-body/eax: (addr line) <- lookup *callee-body-ah 577 var callee-body-first-word/edx: (addr handle word) <- get callee-body, data 578 push-to-call-path-element cursor-call-path, callee-body-first-word 579 # position cursor at left 580 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 581 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 582 var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 583 var cursor-word/eax: (addr word) <- lookup *cursor-word-ah 584 cursor-to-start cursor-word 585 #? print-string 0, "d\n" 586 return 587 } 588 } 589 return 590 } 591 compare key, 0xa/enter 592 { 593 break-if-!= 594 # toggle display of subsidiary stack 595 toggle-cursor-word sandbox 596 return 597 } 598 compare key, 0xc/ctrl-l 599 $process-sandbox-edit:new-line: { 600 break-if-!= 601 # new line in sandbox 602 append-line sandbox 603 return 604 } 605 # word-based motions 606 compare key, 2/ctrl-b 607 $process-sandbox-edit:prev-word: { 608 break-if-!= 609 # jump to previous word at same level 610 var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev 611 var prev-word/eax: (addr word) <- lookup *prev-word-ah 612 { 613 compare prev-word, 0 614 break-if-= 615 cursor-to-end prev-word 616 var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 617 decrement-final-element cursor-call-path 618 return 619 } 620 # if previous word doesn't exist, try to bump up one level 621 { 622 var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path 623 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 624 var caller-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next 625 var caller-cursor-element/eax: (addr call-path-element) <- lookup *caller-cursor-element-ah 626 compare caller-cursor-element, 0 627 break-if-= 628 # check if previous word exists in caller 629 var caller-word-ah/eax: (addr handle word) <- get caller-cursor-element, word 630 var caller-word/eax: (addr word) <- lookup *caller-word-ah 631 var word-before-caller-ah/eax: (addr handle word) <- get caller-word, prev 632 var word-before-caller/eax: (addr word) <- lookup *word-before-caller-ah 633 compare word-before-caller, 0 634 break-if-= 635 # if so jump to it 636 drop-from-call-path-element cursor-call-path-ah 637 decrement-final-element cursor-call-path-ah 638 return 639 } 640 } 641 compare key, 6/ctrl-f 642 $process-sandbox-edit:next-word: { 643 break-if-!= 644 #? print-string 0, "AA\n" 645 # jump to previous word at same level 646 var next-word-ah/edx: (addr handle word) <- get cursor-word, next 647 var next-word/eax: (addr word) <- lookup *next-word-ah 648 { 649 compare next-word, 0 650 break-if-= 651 #? print-string 0, "BB\n" 652 cursor-to-end next-word 653 var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 654 increment-final-element cursor-call-path 655 return 656 } 657 # if next word doesn't exist, try to bump up one level 658 #? print-string 0, "CC\n" 659 var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path 660 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 661 var caller-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next 662 var caller-cursor-element/eax: (addr call-path-element) <- lookup *caller-cursor-element-ah 663 compare caller-cursor-element, 0 664 break-if-= 665 #? print-string 0, "DD\n" 666 copy-object caller-cursor-element-ah, cursor-call-path-ah 667 return 668 } 669 compare key, 7/ctrl-g 670 $process-sandbox-edit:goto-function: { 671 break-if-!= 672 # initialize dialog to name function to jump to 673 var partial-function-name-ah/eax: (addr handle word) <- get self, partial-function-name 674 allocate partial-function-name-ah 675 var partial-function-name/eax: (addr word) <- lookup *partial-function-name-ah 676 initialize-word partial-function-name 677 return 678 } 679 # line-based motions 680 compare key, 1/ctrl-a 681 $process-sandbox-edit:start-of-line: { 682 break-if-!= 683 # move cursor up past all calls and to start of line 684 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 685 drop-nested-calls cursor-call-path-ah 686 move-final-element-to-start-of-line cursor-call-path-ah 687 # move cursor to start of word 688 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 689 var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 690 var cursor-word/eax: (addr word) <- lookup *cursor-word-ah 691 cursor-to-start cursor-word 692 # this works as long as the first word isn't expanded 693 # but we don't expect to see zero-arg functions first-up 694 return 695 } 696 compare key, 5/ctrl-e 697 $process-sandbox-edit:end-of-line: { 698 break-if-!= 699 # move cursor up past all calls and to start of line 700 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 701 drop-nested-calls cursor-call-path-ah 702 move-final-element-to-end-of-line cursor-call-path-ah 703 # move cursor to end of word 704 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 705 var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 706 var cursor-word/eax: (addr word) <- lookup *cursor-word-ah 707 cursor-to-end cursor-word 708 # this works because expanded words lie to the right of their bodies 709 # so the final word is always guaranteed to be at the top-level 710 return 711 } 712 compare key, 0x15/ctrl-u 713 $process-sandbox-edit:clear-line: { 714 break-if-!= 715 # clear line in sandbox 716 initialize-sandbox sandbox 717 return 718 } 719 # if cursor is within a call, disable editing hotkeys below 720 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 721 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 722 var next-cursor-element-ah/eax: (addr handle call-path-element) <- get cursor-call-path, next 723 var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah 724 compare next-cursor-element, 0 725 { 726 break-if-= 727 return 728 } 729 # - remaining keys only work at the top row outside any function calls 730 compare key, 0x7f/del # backspace on Macs 731 $process-sandbox-edit:backspace: { 732 break-if-!= 733 # if not at start of some word, delete grapheme before cursor within current word 734 var at-start?/eax: boolean <- cursor-at-start? cursor-word 735 compare at-start?, 0/false 736 { 737 break-if-!= 738 delete-before-cursor cursor-word 739 return 740 } 741 # otherwise delete current word and move to end of prev word 742 var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev 743 var prev-word/eax: (addr word) <- lookup *prev-word-ah 744 { 745 compare prev-word, 0 746 break-if-= 747 cursor-to-end prev-word 748 delete-next prev-word 749 var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 750 decrement-final-element cursor-call-path 751 } 752 return 753 } 754 compare key, 0x20/space 755 $process-sandbox-edit:space: { 756 break-if-!= 757 #? print-string 0, "space\n" 758 # if cursor is at start of word, insert word before 759 { 760 var at-start?/eax: boolean <- cursor-at-start? cursor-word 761 compare at-start?, 0/false 762 break-if-= 763 var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev 764 append-word prev-word-ah 765 var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 766 decrement-final-element cursor-call-path 767 return 768 } 769 # if start of word is quote and grapheme before cursor is not, just insert it as usual 770 # TODO: support string escaping 771 { 772 var first-grapheme/eax: grapheme <- first-grapheme cursor-word 773 compare first-grapheme, 0x22/double-quote 774 break-if-!= 775 var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word 776 compare final-grapheme, 0x22/double-quote 777 break-if-= 778 break $process-sandbox-edit:space 779 } 780 # if start of word is '[' and grapheme before cursor is not ']', just insert it as usual 781 # TODO: support nested arrays 782 { 783 var first-grapheme/eax: grapheme <- first-grapheme cursor-word 784 compare first-grapheme, 0x5b/[ 785 break-if-!= 786 var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word 787 compare final-grapheme, 0x5d/] 788 break-if-= 789 break $process-sandbox-edit:space 790 } 791 # otherwise insert word after and move cursor to it for the next key 792 # (but we'll continue to track the current cursor-word for the rest of this function) 793 append-word cursor-word-ah 794 var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 795 increment-final-element cursor-call-path 796 # if cursor is at end of word, that's all 797 var at-end?/eax: boolean <- cursor-at-end? cursor-word 798 compare at-end?, 0/false 799 { 800 break-if-= 801 return 802 } 803 # otherwise we're in the middle of a word 804 # move everything after cursor to the (just created) next word 805 var next-word-ah/eax: (addr handle word) <- get cursor-word, next 806 var _next-word/eax: (addr word) <- lookup *next-word-ah 807 var next-word/ebx: (addr word) <- copy _next-word 808 { 809 var at-end?/eax: boolean <- cursor-at-end? cursor-word 810 compare at-end?, 0/false 811 break-if-!= 812 var g/eax: grapheme <- pop-after-cursor cursor-word 813 add-grapheme-to-word next-word, g 814 loop 815 } 816 cursor-to-start next-word 817 return 818 } 819 compare key, 0xe/ctrl-n 820 $process:rename-word: { 821 break-if-!= 822 # TODO: ensure current word is not a function 823 # rename word at cursor 824 var new-name-ah/eax: (addr handle word) <- get sandbox, partial-name-for-cursor-word 825 allocate new-name-ah 826 var new-name/eax: (addr word) <- lookup *new-name-ah 827 initialize-word new-name 828 return 829 } 830 compare key, 4/ctrl-d 831 $process:define-function: { 832 break-if-!= 833 # define function out of line at cursor 834 var new-name-ah/eax: (addr handle word) <- get sandbox, partial-name-for-function 835 allocate new-name-ah 836 var new-name/eax: (addr word) <- lookup *new-name-ah 837 initialize-word new-name 838 return 839 } 840 # otherwise insert key within current word 841 var g/edx: grapheme <- copy key 842 var print?/eax: boolean <- real-grapheme? key 843 $process-sandbox-edit:real-grapheme: { 844 compare print?, 0/false 845 break-if-= 846 add-grapheme-to-word cursor-word, g 847 return 848 } 849 # silently ignore other hotkeys 850 } 851 852 # collect new name in partial-name-for-cursor-word, and then rename the word 853 # at cursor to it 854 # Precondition: cursor-call-path is a singleton (not within a call) 855 fn process-sandbox-rename _sandbox: (addr sandbox), key: grapheme { 856 var sandbox/esi: (addr sandbox) <- copy _sandbox 857 var new-name-ah/edi: (addr handle word) <- get sandbox, partial-name-for-cursor-word 858 # if 'esc' pressed, cancel rename 859 compare key, 0x1b/esc 860 $process-sandbox-rename:cancel: { 861 break-if-!= 862 clear-object new-name-ah 863 return 864 } 865 # if 'enter' pressed, perform rename 866 compare key, 0xa/enter 867 $process-sandbox-rename:commit: { 868 break-if-!= 869 #? print-string 0, "rename\n" 870 # new line 871 var new-line-h: (handle line) 872 var new-line-ah/eax: (addr handle line) <- address new-line-h 873 allocate new-line-ah 874 var new-line/eax: (addr line) <- lookup *new-line-ah 875 initialize-line new-line 876 var new-line-word-ah/ecx: (addr handle word) <- get new-line, data 877 { 878 # move word at cursor to new line 879 var cursor-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 880 var cursor/eax: (addr call-path-element) <- lookup *cursor-ah 881 var word-at-cursor-ah/eax: (addr handle word) <- get cursor, word 882 #? print-string 0, "cursor before at word " 883 #? { 884 #? var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah 885 #? print-word 0, cursor-word 886 #? print-string 0, "\n" 887 #? } 888 move-word-contents word-at-cursor-ah, new-line-word-ah 889 # copy name to word at cursor 890 copy-word-contents-before-cursor new-name-ah, word-at-cursor-ah 891 #? print-string 0, "cursor after at word " 892 #? { 893 #? var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah 894 #? print-word 0, cursor-word 895 #? print-string 0, "\n" 896 #? var foo/eax: int <- copy cursor-word 897 #? print-int32-hex 0, foo 898 #? print-string 0, "\n" 899 #? } 900 #? print-string 0, "new name word " 901 #? { 902 #? var new-name/eax: (addr word) <- lookup *new-name-ah 903 #? print-word 0, new-name 904 #? print-string 0, "\n" 905 #? var foo/eax: int <- copy new-name 906 #? print-int32-hex 0, foo 907 #? print-string 0, "\n" 908 #? } 909 } 910 # prepend '=' to name 911 { 912 var new-name/eax: (addr word) <- lookup *new-name-ah 913 cursor-to-start new-name 914 add-grapheme-to-word new-name, 0x3d/= 915 } 916 # append name to new line 917 chain-words new-line-word-ah, new-name-ah 918 # new-line->next = sandbox->data 919 var new-line-next/ecx: (addr handle line) <- get new-line, next 920 var sandbox-slot/edx: (addr handle line) <- get sandbox, data 921 copy-object sandbox-slot, new-line-next 922 # sandbox->data = new-line 923 copy-handle new-line-h, sandbox-slot 924 # clear partial-name-for-cursor-word 925 clear-object new-name-ah 926 #? var cursor-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 927 #? var cursor/eax: (addr call-path-element) <- lookup *cursor-ah 928 #? var word-at-cursor-ah/eax: (addr handle word) <- get cursor, word 929 #? print-string 0, "cursor after rename: " 930 #? { 931 #? var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah 932 #? print-word 0, cursor-word 933 #? print-string 0, " -- " 934 #? var foo/eax: int <- copy cursor-word 935 #? print-int32-hex 0, foo 936 #? print-string 0, "\n" 937 #? } 938 return 939 } 940 # 941 compare key, 0x7f/del # backspace on Macs 942 $process-sandbox-rename:backspace: { 943 break-if-!= 944 # if not at start, delete grapheme before cursor 945 var new-name/eax: (addr word) <- lookup *new-name-ah 946 var at-start?/eax: boolean <- cursor-at-start? new-name 947 compare at-start?, 0/false 948 { 949 break-if-!= 950 var new-name/eax: (addr word) <- lookup *new-name-ah 951 delete-before-cursor new-name 952 } 953 return 954 } 955 # otherwise insert key within current word 956 var print?/eax: boolean <- real-grapheme? key 957 $process-sandbox-rename:real-grapheme: { 958 compare print?, 0/false 959 break-if-= 960 var new-name/eax: (addr word) <- lookup *new-name-ah 961 add-grapheme-to-word new-name, key 962 return 963 } 964 # silently ignore other hotkeys 965 } 966 967 # collect new name in partial-name-for-function, and then define the last line 968 # of the sandbox to be a new function with that name. Replace the last line 969 # with a call to the appropriate function. 970 # Precondition: cursor-call-path is a singleton (not within a call) 971 fn process-sandbox-define _sandbox: (addr sandbox), functions: (addr handle function), key: grapheme { 972 var sandbox/esi: (addr sandbox) <- copy _sandbox 973 var new-name-ah/edi: (addr handle word) <- get sandbox, partial-name-for-function 974 # if 'esc' pressed, cancel define 975 compare key, 0x1b/esc 976 $process-sandbox-define:cancel: { 977 break-if-!= 978 clear-object new-name-ah 979 return 980 } 981 # if 'enter' pressed, perform define 982 compare key, 0xa/enter 983 $process-sandbox-define:commit: { 984 break-if-!= 985 #? print-string 0, "define\n" 986 # create new function 987 var new-function: (handle function) 988 var new-function-ah/ecx: (addr handle function) <- address new-function 989 allocate new-function-ah 990 var _new-function/eax: (addr function) <- lookup *new-function-ah 991 var new-function/ebx: (addr function) <- copy _new-function 992 var dest/edx: (addr handle function) <- get new-function, next 993 copy-object functions, dest 994 copy-object new-function-ah, functions 995 # set function name to new-name 996 var new-name/eax: (addr word) <- lookup *new-name-ah 997 var dest/edx: (addr handle array byte) <- get new-function, name 998 word-to-string new-name, dest 999 # move final line to body 1000 var body-ah/eax: (addr handle line) <- get new-function, body 1001 allocate body-ah 1002 var body/eax: (addr line) <- lookup *body-ah 1003 var body-contents/ecx: (addr handle word) <- get body, data 1004 var final-line-storage: (handle line) 1005 var final-line-ah/eax: (addr handle line) <- address final-line-storage 1006 final-line sandbox, final-line-ah 1007 var final-line/eax: (addr line) <- lookup *final-line-ah 1008 var final-line-contents/eax: (addr handle word) <- get final-line, data 1009 copy-object final-line-contents, body-contents 1010 var cursor-word-ah/ecx: (addr handle word) <- get new-function, cursor-word 1011 copy-object final-line-contents, cursor-word-ah 1012 { 1013 var cursor-word/eax: (addr word) <- lookup *cursor-word-ah 1014 cursor-to-start cursor-word 1015 } 1016 # 1017 copy-unbound-words-to-args functions 1018 # 1019 var empty-word: (handle word) 1020 copy-handle empty-word, final-line-contents 1021 construct-call functions, final-line-contents 1022 # clear partial-name-for-function 1023 var empty-word: (handle word) 1024 copy-handle empty-word, new-name-ah 1025 # update cursor 1026 var final-line/eax: (addr line) <- lookup final-line-storage 1027 var cursor-call-path-ah/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path 1028 allocate cursor-call-path-ah # leak 1029 initialize-path-from-line final-line, cursor-call-path-ah 1030 return 1031 } 1032 # 1033 compare key, 0x7f/del # backspace on Macs 1034 $process-sandbox-define:backspace: { 1035 break-if-!= 1036 # if not at start, delete grapheme before cursor 1037 var new-name/eax: (addr word) <- lookup *new-name-ah 1038 var at-start?/eax: boolean <- cursor-at-start? new-name 1039 compare at-start?, 0/false 1040 { 1041 break-if-!= 1042 var new-name/eax: (addr word) <- lookup *new-name-ah 1043 delete-before-cursor new-name 1044 } 1045 return 1046 } 1047 # otherwise insert key within current word 1048 var print?/eax: boolean <- real-grapheme? key 1049 $process-sandbox-define:real-grapheme: { 1050 compare print?, 0/false 1051 break-if-= 1052 var new-name/eax: (addr word) <- lookup *new-name-ah 1053 add-grapheme-to-word new-name, key 1054 return 1055 } 1056 # silently ignore other hotkeys 1057 } 1058 1059 # extract from the body of the first function in 'functions' all words that 1060 # aren't defined in the rest of 'functions'. Prepend them in reverse order. 1061 # Assumes function body is a single line for now. 1062 fn copy-unbound-words-to-args _functions: (addr handle function) { 1063 # target 1064 var target-ah/eax: (addr handle function) <- copy _functions 1065 var _target/eax: (addr function) <- lookup *target-ah 1066 var target/esi: (addr function) <- copy _target 1067 var dest-ah/edi: (addr handle word) <- get target, args 1068 # next 1069 var functions-ah/edx: (addr handle function) <- get target, next 1070 # src 1071 var line-ah/eax: (addr handle line) <- get target, body 1072 var line/eax: (addr line) <- lookup *line-ah 1073 var curr-ah/eax: (addr handle word) <- get line, data 1074 var curr/eax: (addr word) <- lookup *curr-ah 1075 { 1076 compare curr, 0 1077 break-if-= 1078 $copy-unbound-words-to-args:loop-iter: { 1079 # is it a number? 1080 { 1081 var int?/eax: boolean <- word-is-decimal-integer? curr 1082 compare int?, 0/false 1083 break-if-!= $copy-unbound-words-to-args:loop-iter 1084 } 1085 # is it a pre-existing function? 1086 var bound?/ebx: boolean <- bound-function? curr, functions-ah 1087 compare bound?, 0/false 1088 break-if-!= 1089 # is it already bound as an arg? 1090 var dup?/ebx: boolean <- arg-exists? _functions, curr # _functions = target-ah 1091 compare dup?, 0/false 1092 break-if-!= $copy-unbound-words-to-args:loop-iter 1093 # push copy of curr before dest-ah 1094 var rest-h: (handle word) 1095 var rest-ah/ecx: (addr handle word) <- address rest-h 1096 copy-object dest-ah, rest-ah 1097 copy-word curr, dest-ah 1098 chain-words dest-ah, rest-ah 1099 } 1100 var next-ah/ecx: (addr handle word) <- get curr, next 1101 curr <- lookup *next-ah 1102 loop 1103 } 1104 } 1105 1106 fn bound-function? w: (addr word), functions-ah: (addr handle function) -> _/ebx: boolean { 1107 var result/ebx: boolean <- copy 1/true 1108 { 1109 ## numbers 1110 # if w == "+" return true 1111 var subresult/eax: boolean <- word-equal? w, "+" 1112 compare subresult, 0/false 1113 break-if-!= 1114 # if w == "-" return true 1115 subresult <- word-equal? w, "-" 1116 compare subresult, 0/false 1117 break-if-!= 1118 # if w == "*" return true 1119 subresult <- word-equal? w, "*" 1120 compare subresult, 0/false 1121 break-if-!= 1122 # if w == "/" return true 1123 subresult <- word-equal? w, "/" 1124 compare subresult, 0/false 1125 break-if-!= 1126 # if w == "sqrt" return true 1127 subresult <- word-equal? w, "sqrt" 1128 compare subresult, 0/false 1129 break-if-!= 1130 ## strings/arrays 1131 # if w == "len" return true 1132 subresult <- word-equal? w, "len" 1133 compare subresult, 0/false 1134 break-if-!= 1135 ## files 1136 # if w == "open" return true 1137 subresult <- word-equal? w, "open" 1138 compare subresult, 0/false 1139 break-if-!= 1140 # if w == "read" return true 1141 subresult <- word-equal? w, "read" 1142 compare subresult, 0/false 1143 break-if-!= 1144 # if w == "slurp" return true 1145 subresult <- word-equal? w, "slurp" 1146 compare subresult, 0/false 1147 break-if-!= 1148 # if w == "lines" return true 1149 subresult <- word-equal? w, "lines" 1150 compare subresult, 0/false 1151 break-if-!= 1152 ## screens 1153 # if w == "fake-screen" return true 1154 subresult <- word-equal? w, "fake-screen" 1155 compare subresult, 0/false 1156 break-if-!= 1157 # if w == "print" return true 1158 subresult <- word-equal? w, "print" 1159 compare subresult, 0/false 1160 break-if-!= 1161 # if w == "move" return true 1162 subresult <- word-equal? w, "move" 1163 compare subresult, 0/false 1164 break-if-!= 1165 # if w == "up" return true 1166 subresult <- word-equal? w, "up" 1167 compare subresult, 0/false 1168 break-if-!= 1169 # if w == "down" return true 1170 subresult <- word-equal? w, "down" 1171 compare subresult, 0/false 1172 break-if-!= 1173 # if w == "left" return true 1174 subresult <- word-equal? w, "left" 1175 compare subresult, 0/false 1176 break-if-!= 1177 # if w == "right" return true 1178 subresult <- word-equal? w, "right" 1179 compare subresult, 0/false 1180 break-if-!= 1181 ## hacks 1182 # if w == "dup" return true 1183 subresult <- word-equal? w, "dup" 1184 compare subresult, 0/false 1185 break-if-!= 1186 # if w == "swap" return true 1187 subresult <- word-equal? w, "swap" 1188 compare subresult, 0/false 1189 break-if-!= 1190 # return w in functions 1191 var out-h: (handle function) 1192 var out/eax: (addr handle function) <- address out-h 1193 callee functions-ah, w, out 1194 var found?/eax: (addr function) <- lookup *out 1195 result <- copy found? 1196 } 1197 return result 1198 } 1199 1200 fn arg-exists? _f-ah: (addr handle function), arg: (addr word) -> _/ebx: boolean { 1201 var f-ah/eax: (addr handle function) <- copy _f-ah 1202 var f/eax: (addr function) <- lookup *f-ah 1203 var args-ah/eax: (addr handle word) <- get f, args 1204 var result/ebx: boolean <- word-exists? args-ah, arg 1205 return result 1206 } 1207 1208 # construct a call to `f` with copies of exactly its args 1209 fn construct-call _f-ah: (addr handle function), _dest-ah: (addr handle word) { 1210 var f-ah/eax: (addr handle function) <- copy _f-ah 1211 var _f/eax: (addr function) <- lookup *f-ah 1212 var f/esi: (addr function) <- copy _f 1213 # append args in reverse 1214 var args-ah/eax: (addr handle word) <- get f, args 1215 var dest-ah/edi: (addr handle word) <- copy _dest-ah 1216 copy-words-in-reverse args-ah, dest-ah 1217 # append name 1218 var name-ah/eax: (addr handle array byte) <- get f, name 1219 var name/eax: (addr array byte) <- lookup *name-ah 1220 append-word-at-end-with dest-ah, name 1221 } 1222 1223 fn word-index _words: (addr handle word), _n: int, out: (addr handle word) { 1224 var n/ecx: int <- copy _n 1225 { 1226 compare n, 0 1227 break-if-!= 1228 copy-object _words, out 1229 return 1230 } 1231 var words-ah/eax: (addr handle word) <- copy _words 1232 var words/eax: (addr word) <- lookup *words-ah 1233 var next/eax: (addr handle word) <- get words, next 1234 n <- decrement 1235 word-index next, n, out 1236 } 1237 1238 fn toggle-cursor-word _sandbox: (addr sandbox) { 1239 var sandbox/esi: (addr sandbox) <- copy _sandbox 1240 var expanded-words/edi: (addr handle call-path) <- get sandbox, expanded-words 1241 var cursor-call-path/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path 1242 #? print-string 0, "cursor call path: " 1243 #? dump-call-path-element 0, cursor-call-path 1244 #? print-string 0, "expanded words:\n" 1245 #? dump-call-paths 0, expanded-words 1246 var already-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path 1247 compare already-expanded?, 0/false 1248 { 1249 break-if-!= 1250 #? print-string 0, "expand\n" 1251 # if not already-expanded, insert 1252 insert-in-call-path expanded-words cursor-call-path 1253 #? print-string 0, "expanded words now:\n" 1254 #? dump-call-paths 0, expanded-words 1255 return 1256 } 1257 { 1258 break-if-= 1259 # otherwise delete 1260 delete-in-call-path expanded-words cursor-call-path 1261 } 1262 } 1263 1264 fn append-line _sandbox: (addr sandbox) { 1265 var sandbox/esi: (addr sandbox) <- copy _sandbox 1266 var line-ah/ecx: (addr handle line) <- get sandbox, data 1267 { 1268 var line/eax: (addr line) <- lookup *line-ah 1269 var next-line-ah/edx: (addr handle line) <- get line, next 1270 var next-line/eax: (addr line) <- lookup *next-line-ah 1271 compare next-line, 0 1272 break-if-= 1273 line-ah <- copy next-line-ah 1274 loop 1275 } 1276 var line/eax: (addr line) <- lookup *line-ah 1277 var final-line-ah/edx: (addr handle line) <- get line, next 1278 allocate final-line-ah 1279 var final-line/eax: (addr line) <- lookup *final-line-ah 1280 initialize-line final-line 1281 var final-prev/eax: (addr handle line) <- get final-line, prev 1282 copy-object line-ah, final-prev 1283 # clear cursor 1284 var final-line/eax: (addr line) <- lookup *final-line-ah 1285 var word-ah/ecx: (addr handle word) <- get final-line, data 1286 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 1287 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 1288 var dest/eax: (addr handle word) <- get cursor-call-path, word 1289 copy-object word-ah, dest 1290 } 1291 1292 ############# 1293 # Visualize 1294 ############# 1295 1296 fn evaluate-environment _env: (addr environment), stack: (addr value-stack) { 1297 var env/esi: (addr environment) <- copy _env 1298 # functions 1299 var functions/edx: (addr handle function) <- get env, functions 1300 # line 1301 var sandbox-ah/esi: (addr handle sandbox) <- get env, sandboxes 1302 var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah 1303 var line-ah/eax: (addr handle line) <- get sandbox, data 1304 var _line/eax: (addr line) <- lookup *line-ah 1305 var line/esi: (addr line) <- copy _line 1306 evaluate functions, 0, line, 0, stack 1307 } 1308 1309 fn render _env: (addr environment) { 1310 #? print-string 0, "== render\n" 1311 var env/esi: (addr environment) <- copy _env 1312 clear-canvas env 1313 # menu 1314 render-menu env 1315 # screen 1316 var screen-ah/eax: (addr handle screen) <- get env, screen 1317 var _screen/eax: (addr screen) <- lookup *screen-ah 1318 var screen/edi: (addr screen) <- copy _screen 1319 # functions 1320 var sep-col/eax: (addr int) <- get env, code-separator-col 1321 var functions/edx: (addr handle function) <- get env, functions 1322 render-functions screen, *sep-col, env 1323 # sandbox 1324 var repl-col/ecx: int <- copy *sep-col 1325 repl-col <- add 2/repl-margin-left 1326 var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox 1327 var cursor-sandbox/eax: (addr sandbox) <- lookup *cursor-sandbox-ah 1328 # bindings 1329 var bindings-storage: table 1330 var bindings/ebx: (addr table) <- address bindings-storage 1331 initialize-table bindings, 0x10 1332 #? print-string 0, "render-sandbox {\n" 1333 render-sandbox screen, functions, bindings, cursor-sandbox, 3, repl-col 1334 #? print-string 0, "render-sandbox }\n" 1335 # dialogs 1336 render-goto-dialog screen, env 1337 # 1338 position-cursor screen, env 1339 } 1340 1341 # draw a wordstar-style cheatsheet of shortcuts on the bottom line of the screen 1342 fn render-menu _env: (addr environment) { 1343 var env/esi: (addr environment) <- copy _env 1344 var cursor-function-ah/eax: (addr handle function) <- get env, cursor-function 1345 var cursor-function/eax: (addr function) <- lookup *cursor-function-ah 1346 { 1347 compare cursor-function, 0 1348 break-if-= 1349 render-function-menu env 1350 return 1351 } 1352 var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox 1353 var cursor-sandbox/eax: (addr sandbox) <- lookup *cursor-sandbox-ah 1354 { 1355 compare cursor-sandbox, 0 1356 break-if-= 1357 render-sandbox-menu env 1358 return 1359 } 1360 } 1361 1362 # HACK: areas currently responsible for positioning their dialogs' cursors. So 1363 # we just do nothing here if a dialog is up. 1364 fn position-cursor screen: (addr screen), _env: (addr environment) { 1365 var env/esi: (addr environment) <- copy _env 1366 var goto-function-ah/eax: (addr handle word) <- get env, partial-function-name 1367 var goto-function/eax: (addr word) <- lookup *goto-function-ah 1368 { 1369 compare goto-function, 0 1370 break-if-= 1371 return 1372 } 1373 var cursor-function-ah/eax: (addr handle function) <- get env, cursor-function 1374 var cursor-function/eax: (addr function) <- lookup *cursor-function-ah 1375 { 1376 compare cursor-function, 0 1377 break-if-= 1378 var cursor-row/ecx: (addr int) <- get cursor-function, cursor-row 1379 var cursor-col/eax: (addr int) <- get cursor-function, cursor-col 1380 move-cursor screen, *cursor-row, *cursor-col 1381 return 1382 } 1383 var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox 1384 var cursor-sandbox/eax: (addr sandbox) <- lookup *cursor-sandbox-ah 1385 { 1386 compare cursor-sandbox, 0 1387 break-if-= 1388 # if in a dialog, return 1389 { 1390 var partial-word-rename-ah/eax: (addr handle word) <- get cursor-sandbox, partial-name-for-cursor-word 1391 var partial-word-rename/eax: (addr word) <- lookup *partial-word-rename-ah 1392 compare partial-word-rename, 0 1393 break-if-= 1394 return 1395 } 1396 { 1397 var partial-function-name-ah/eax: (addr handle word) <- get cursor-sandbox, partial-name-for-function 1398 var partial-function-name/eax: (addr word) <- lookup *partial-function-name-ah 1399 compare partial-function-name, 0 1400 break-if-= 1401 return 1402 } 1403 var cursor-row/ecx: (addr int) <- get cursor-sandbox, cursor-row 1404 var cursor-col/eax: (addr int) <- get cursor-sandbox, cursor-col 1405 move-cursor screen, *cursor-row, *cursor-col 1406 } 1407 } 1408 1409 fn render-goto-dialog screen: (addr screen), _env: (addr environment) { 1410 var env/esi: (addr environment) <- copy _env 1411 var goto-function-mode-ah?/eax: (addr handle word) <- get env, partial-function-name 1412 var goto-function-mode?/eax: (addr word) <- lookup *goto-function-mode-ah? 1413 compare goto-function-mode?, 0/false 1414 break-if-= 1415 # clear a space for the dialog 1416 var top-row/ebx: int <- copy 3 1417 var bottom-row/edx: int <- copy 9 1418 var sep-col/eax: (addr int) <- get env, code-separator-col 1419 var left-col/ecx: int <- copy *sep-col 1420 left-col <- subtract 0x10 1421 var right-col/eax: int <- copy *sep-col 1422 right-col <- add 0x10 1423 clear-rect screen, top-row, left-col, bottom-row, right-col 1424 draw-box screen, top-row, left-col, bottom-row, right-col 1425 # render a little menu for the dialog 1426 var menu-row/eax: int <- copy bottom-row 1427 menu-row <- decrement 1428 var menu-col/edx: int <- copy left-col 1429 menu-col <- add 2 1430 move-cursor screen, menu-row, menu-col 1431 start-reverse-video screen 1432 print-string screen, " esc " 1433 reset-formatting screen 1434 print-string screen, " cancel " 1435 start-reverse-video screen 1436 print-string screen, " enter " 1437 reset-formatting screen 1438 print-string screen, " jump " 1439 # draw the word, positioned appropriately around the cursor 1440 var start-col/ecx: int <- copy left-col 1441 start-col <- increment 1442 move-cursor screen, 6, start-col # cursor-row 1443 var word-ah?/edx: (addr handle word) <- get env, partial-function-name 1444 var word/eax: (addr word) <- lookup *word-ah? 1445 print-word screen, word 1446 } 1447 1448 fn render-sandbox screen: (addr screen), functions: (addr handle function), bindings: (addr table), _sandbox: (addr sandbox), top-row: int, left-col: int { 1449 var sandbox/esi: (addr sandbox) <- copy _sandbox 1450 # line 1451 var curr-line-ah/eax: (addr handle line) <- get sandbox, data 1452 var _curr-line/eax: (addr line) <- lookup *curr-line-ah 1453 var curr-line/ecx: (addr line) <- copy _curr-line 1454 # 1455 var curr-row/edx: int <- copy top-row 1456 # cursor row, col 1457 var cursor-row-addr: (addr int) 1458 var tmp/eax: (addr int) <- get sandbox, cursor-row 1459 copy-to cursor-row-addr, tmp 1460 var cursor-col-addr: (addr int) 1461 tmp <- get sandbox, cursor-col 1462 copy-to cursor-col-addr, tmp 1463 # render all but final line without stack 1464 #? print-string 0, "render all but final line\n" 1465 { 1466 var next-line-ah/eax: (addr handle line) <- get curr-line, next 1467 var next-line/eax: (addr line) <- lookup *next-line-ah 1468 compare next-line, 0 1469 break-if-= 1470 { 1471 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 1472 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 1473 var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 1474 var cursor-word/eax: (addr word) <- lookup *cursor-word-ah 1475 # it's enough to pass in the first word of the path, because if the path isn't a singleton the word is guaranteed to be unique 1476 render-line-without-stack screen, curr-line, curr-row, left-col, cursor-word, cursor-row-addr, cursor-col-addr 1477 } 1478 curr-line <- copy next-line 1479 curr-row <- add 2 1480 loop 1481 } 1482 # 1483 render-final-line-with-stack screen, functions, bindings, sandbox, curr-row, left-col, cursor-row-addr, cursor-col-addr 1484 # at most one of the following dialogs will be rendered 1485 render-rename-dialog screen, sandbox 1486 render-define-dialog screen, sandbox 1487 } 1488 1489 fn render-final-line-with-stack screen: (addr screen), functions: (addr handle function), bindings: (addr table), _sandbox: (addr sandbox), top-row: int, left-col: int, cursor-row-addr: (addr int), cursor-col-addr: (addr int) { 1490 var sandbox/esi: (addr sandbox) <- copy _sandbox 1491 # expanded-words 1492 var expanded-words/edi: (addr handle call-path) <- get sandbox, expanded-words 1493 # cursor-word 1494 var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 1495 var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah 1496 var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word 1497 var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah 1498 var cursor-word/ebx: (addr word) <- copy _cursor-word 1499 #? print-string 0, "word at cursor: " 1500 #? print-word 0, cursor-word 1501 #? print-string 0, "\n" 1502 # cursor-call-path 1503 var cursor-call-path: (addr handle call-path-element) 1504 { 1505 var src/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path 1506 copy-to cursor-call-path, src 1507 } 1508 # first line 1509 var first-line-ah/eax: (addr handle line) <- get sandbox, data 1510 var _first-line/eax: (addr line) <- lookup *first-line-ah 1511 var first-line/edx: (addr line) <- copy _first-line 1512 # final line 1513 var final-line-storage: (handle line) 1514 var final-line-ah/eax: (addr handle line) <- address final-line-storage 1515 final-line sandbox, final-line-ah 1516 var final-line/eax: (addr line) <- lookup *final-line-ah 1517 # curr-path 1518 var curr-path-storage: (handle call-path-element) 1519 var curr-path/ecx: (addr handle call-path-element) <- address curr-path-storage 1520 allocate curr-path # leak 1521 initialize-path-from-line final-line, curr-path 1522 # 1523 var dummy/ecx: int <- render-line screen, functions, bindings, first-line, final-line, expanded-words, top-row, left-col, curr-path, cursor-word, cursor-call-path, cursor-row-addr, cursor-col-addr 1524 } 1525 1526 fn final-line _sandbox: (addr sandbox), out: (addr handle line) { 1527 var sandbox/esi: (addr sandbox) <- copy _sandbox 1528 var curr-line-ah/ecx: (addr handle line) <- get sandbox, data 1529 { 1530 var curr-line/eax: (addr line) <- lookup *curr-line-ah 1531 var next-line-ah/edx: (addr handle line) <- get curr-line, next 1532 var next-line/eax: (addr line) <- lookup *next-line-ah 1533 compare next-line, 0 1534 break-if-= 1535 curr-line-ah <- copy next-line-ah 1536 loop 1537 } 1538 copy-object curr-line-ah, out 1539 } 1540 1541 fn render-rename-dialog screen: (addr screen), _sandbox: (addr sandbox) { 1542 var sandbox/edi: (addr sandbox) <- copy _sandbox 1543 var rename-word-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-cursor-word 1544 var rename-word-mode?/eax: (addr word) <- lookup *rename-word-mode-ah? 1545 compare rename-word-mode?, 0/false 1546 break-if-= 1547 # clear a space for the dialog 1548 var cursor-row/ebx: (addr int) <- get sandbox, cursor-row 1549 var top-row/eax: int <- copy *cursor-row 1550 top-row <- subtract 3 1551 var bottom-row/ecx: int <- copy *cursor-row 1552 bottom-row <- add 3 1553 var cursor-col/ebx: (addr int) <- get sandbox, cursor-col 1554 var left-col/edx: int <- copy *cursor-col 1555 left-col <- subtract 0x10 1556 var right-col/ebx: int <- copy *cursor-col 1557 right-col <- add 0x10 1558 clear-rect screen, top-row, left-col, bottom-row, right-col 1559 draw-box screen, top-row, left-col, bottom-row, right-col 1560 # render a little menu for the dialog 1561 var menu-row/ecx: int <- copy bottom-row 1562 menu-row <- decrement 1563 var menu-col/edx: int <- copy left-col 1564 menu-col <- add 2 1565 move-cursor screen, menu-row, menu-col 1566 start-reverse-video screen 1567 print-string screen, " esc " 1568 reset-formatting screen 1569 print-string screen, " cancel " 1570 start-reverse-video screen 1571 print-string screen, " enter " 1572 reset-formatting screen 1573 print-string screen, " rename " 1574 # draw the word, positioned appropriately around the cursor 1575 var cursor-col/ebx: (addr int) <- get sandbox, cursor-col 1576 var start-col/ecx: int <- copy *cursor-col 1577 var word-ah?/edx: (addr handle word) <- get sandbox, partial-name-for-cursor-word 1578 var word/eax: (addr word) <- lookup *word-ah? 1579 var cursor-index/eax: int <- cursor-index word 1580 start-col <- subtract cursor-index 1581 var cursor-row/ebx: (addr int) <- get sandbox, cursor-row 1582 move-cursor screen, *cursor-row, start-col 1583 var word/eax: (addr word) <- lookup *word-ah? 1584 print-word screen, word 1585 } 1586 1587 fn render-define-dialog screen: (addr screen), _sandbox: (addr sandbox) { 1588 var sandbox/edi: (addr sandbox) <- copy _sandbox 1589 var define-function-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-function 1590 var define-function-mode?/eax: (addr word) <- lookup *define-function-mode-ah? 1591 compare define-function-mode?, 0/false 1592 break-if-= 1593 # clear a space for the dialog 1594 var cursor-row/ebx: (addr int) <- get sandbox, cursor-row 1595 var top-row/eax: int <- copy *cursor-row 1596 top-row <- subtract 3 1597 var bottom-row/ecx: int <- copy *cursor-row 1598 bottom-row <- add 3 1599 var cursor-col/ebx: (addr int) <- get sandbox, cursor-col 1600 var left-col/edx: int <- copy *cursor-col 1601 left-col <- subtract 0x10 1602 var right-col/ebx: int <- copy *cursor-col 1603 right-col <- add 0x10 1604 clear-rect screen, top-row, left-col, bottom-row, right-col 1605 draw-box screen, top-row, left-col, bottom-row, right-col 1606 # render a little menu for the dialog 1607 var menu-row/ecx: int <- copy bottom-row 1608 menu-row <- decrement 1609 var menu-col/edx: int <- copy left-col 1610 menu-col <- add 2 1611 move-cursor screen, menu-row, menu-col 1612 start-reverse-video screen 1613 print-string screen, " esc " 1614 reset-formatting screen 1615 print-string screen, " cancel " 1616 start-reverse-video screen 1617 print-string screen, " enter " 1618 reset-formatting screen 1619 print-string screen, " define " 1620 # draw the word, positioned appropriately around the cursor 1621 var cursor-col/ebx: (addr int) <- get sandbox, cursor-col 1622 var start-col/ecx: int <- copy *cursor-col 1623 var word-ah?/edx: (addr handle word) <- get sandbox, partial-name-for-function 1624 var word/eax: (addr word) <- lookup *word-ah? 1625 var cursor-index/eax: int <- cursor-index word 1626 start-col <- subtract cursor-index 1627 var cursor-row/ebx: (addr int) <- get sandbox, cursor-row 1628 move-cursor screen, *cursor-row, start-col 1629 var word/eax: (addr word) <- lookup *word-ah? 1630 print-word screen, word 1631 } 1632 1633 # Render just the words in 'line'. 1634 fn render-line-without-stack screen: (addr screen), _line: (addr line), curr-row: int, left-col: int, cursor-word: (addr word), cursor-row-addr: (addr int), cursor-col-addr: (addr int) { 1635 # curr-word 1636 var line/eax: (addr line) <- copy _line 1637 var first-word-ah/eax: (addr handle word) <- get line, data 1638 var _curr-word/eax: (addr word) <- lookup *first-word-ah 1639 var curr-word/esi: (addr word) <- copy _curr-word 1640 # 1641 # loop-carried dependency 1642 var curr-col/ecx: int <- copy left-col 1643 # 1644 { 1645 compare curr-word, 0 1646 break-if-= 1647 #? print-string 0, "-- word in penultimate lines: " 1648 #? { 1649 #? var foo/eax: int <- copy curr-word 1650 #? print-int32-hex 0, foo 1651 #? } 1652 #? print-string 0, "\n" 1653 var old-col/edx: int <- copy curr-col 1654 move-cursor screen, curr-row, curr-col 1655 print-word screen, curr-word 1656 { 1657 var max-width/eax: int <- word-length curr-word 1658 curr-col <- add max-width 1659 curr-col <- add 1/margin-right 1660 } 1661 # cache cursor column if necessary 1662 { 1663 compare curr-word, cursor-word 1664 break-if-!= 1665 #? print-string 0, "Cursor at " 1666 #? print-int32-decimal 0, curr-row 1667 #? print-string 0, ", " 1668 #? print-int32-decimal 0, old-col 1669 #? print-string 0, "\n" 1670 #? print-string 0, "contents: " 1671 #? print-word 0, cursor-word 1672 #? print-string 0, "\n" 1673 #? { 1674 #? var foo/eax: int <- copy cursor-word 1675 #? print-int32-hex 0, foo 1676 #? print-string 0, "\n" 1677 #? } 1678 var dest/ecx: (addr int) <- copy cursor-row-addr 1679 var src/eax: int <- copy curr-row 1680 copy-to *dest, src 1681 dest <- copy cursor-col-addr 1682 copy-to *dest, old-col 1683 var cursor-index-in-word/eax: int <- cursor-index curr-word 1684 add-to *dest, cursor-index-in-word 1685 } 1686 # loop update 1687 var next-word-ah/edx: (addr handle word) <- get curr-word, next 1688 var _curr-word/eax: (addr word) <- lookup *next-word-ah 1689 curr-word <- copy _curr-word 1690 loop 1691 } 1692 } 1693 1694 fn call-depth-at-cursor _sandbox: (addr sandbox) -> _/eax: int { 1695 var sandbox/esi: (addr sandbox) <- copy _sandbox 1696 var cursor-call-path/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path 1697 var result/eax: int <- call-path-element-length cursor-call-path 1698 result <- add 2 # input-row - 1 1699 return result 1700 } 1701 1702 fn call-path-element-length _x: (addr handle call-path-element) -> _/eax: int { 1703 var curr-ah/ecx: (addr handle call-path-element) <- copy _x 1704 var result/edi: int <- copy 0 1705 { 1706 var curr/eax: (addr call-path-element) <- lookup *curr-ah 1707 compare curr, 0 1708 break-if-= 1709 curr-ah <- get curr, next 1710 result <- increment 1711 loop 1712 } 1713 return result 1714 } 1715 1716 # Render the line of words in line, along with the state of the stack under each word. 1717 # Also render any expanded function calls using recursive calls. 1718 # 1719 # Along the way, compute the column the cursor should be positioned at (cursor-col-addr). 1720 fn render-line screen: (addr screen), functions: (addr handle function), bindings: (addr table), first-line: (addr line), _line: (addr line), expanded-words: (addr handle call-path), top-row: int, left-col: int, curr-path: (addr handle call-path-element), cursor-word: (addr word), cursor-call-path: (addr handle call-path-element), cursor-row-addr: (addr int), cursor-col-addr: (addr int) -> _/ecx: int { 1721 #? print-string 0, "render-line\n" 1722 #? dump-table bindings 1723 # curr-word 1724 var line/esi: (addr line) <- copy _line 1725 var first-word-ah/eax: (addr handle word) <- get line, data 1726 var curr-word/eax: (addr word) <- lookup *first-word-ah 1727 # 1728 # loop-carried dependency 1729 var curr-col/ecx: int <- copy left-col 1730 # 1731 { 1732 compare curr-word, 0 1733 break-if-= 1734 #? print-string 0, "-- word " 1735 #? print-word 0, curr-word 1736 #? print-string 0, "\n" 1737 # if necessary, first render columns for subsidiary stack 1738 $render-line:subsidiary: { 1739 { 1740 #? print-string 0, "check sub\n" 1741 var display-subsidiary-stack?/eax: boolean <- find-in-call-paths expanded-words, curr-path 1742 compare display-subsidiary-stack?, 0/false 1743 break-if-= $render-line:subsidiary 1744 } 1745 #? print-string 0, "render subsidiary stack\n" 1746 # does function exist? 1747 var callee/edi: (addr function) <- copy 0 1748 { 1749 var callee-h: (handle function) 1750 var callee-ah/ecx: (addr handle function) <- address callee-h 1751 callee functions, curr-word, callee-ah 1752 var _callee/eax: (addr function) <- lookup *callee-ah 1753 callee <- copy _callee 1754 compare callee, 0 1755 break-if-= $render-line:subsidiary 1756 } 1757 move-cursor screen, top-row, curr-col 1758 start-color screen, 8, 7 1759 print-word screen, curr-word 1760 { 1761 var word-len/eax: int <- word-length curr-word 1762 curr-col <- add word-len 1763 curr-col <- add 2 1764 increment top-row 1765 } 1766 # obtain stack at call site 1767 var stack-storage: value-stack 1768 var stack/edx: (addr value-stack) <- address stack-storage 1769 initialize-value-stack stack, 0x10 1770 { 1771 var prev-word-ah/eax: (addr handle word) <- get curr-word, prev 1772 var prev-word/eax: (addr word) <- lookup *prev-word-ah 1773 compare prev-word, 0 1774 break-if-= 1775 var bindings2-storage: table 1776 var bindings2/ebx: (addr table) <- address bindings2-storage 1777 deep-copy-table bindings, bindings2 1778 evaluate functions, bindings2, first-line, prev-word, stack 1779 } 1780 # construct new bindings 1781 var callee-bindings-storage: table 1782 var callee-bindings/esi: (addr table) <- address callee-bindings-storage 1783 initialize-table callee-bindings, 0x10 1784 bind-args callee, stack, callee-bindings 1785 # obtain body 1786 var callee-body-ah/eax: (addr handle line) <- get callee, body 1787 var callee-body/eax: (addr line) <- lookup *callee-body-ah 1788 var callee-body-first-word/edx: (addr handle word) <- get callee-body, data 1789 # - render subsidiary stack 1790 push-to-call-path-element curr-path, callee-body-first-word # leak 1791 #? print-string 0, "subsidiary {\n" 1792 #? dump-table callee-bindings 1793 #? syscall_exit 1794 curr-col <- render-line screen, functions, callee-bindings, callee-body, callee-body, expanded-words, top-row, curr-col, curr-path, cursor-word, cursor-call-path, cursor-row-addr, cursor-col-addr 1795 #? print-string 0, "}\n" 1796 drop-from-call-path-element curr-path 1797 # 1798 move-cursor screen, top-row, curr-col 1799 print-code-point screen, 0x21d7/⇗ 1800 # 1801 curr-col <- add 2 1802 decrement top-row 1803 } 1804 # render main column 1805 var old-col/edx: int <- copy curr-col 1806 var bindings2-storage: table 1807 var bindings2/ebx: (addr table) <- address bindings2-storage 1808 #? print-string 0, "deep-copy {\n" 1809 deep-copy-table bindings, bindings2 1810 #? print-string 0, "}\n" 1811 #? print-string 0, "render column {\n" 1812 curr-col <- render-column screen, functions, bindings2, first-line, line, curr-word, top-row, curr-col 1813 #? print-string 0, "}\n" 1814 # cache cursor column if necessary 1815 $render-line:cache-cursor-column: { 1816 { 1817 var found?/eax: boolean <- call-path-element-match? curr-path, cursor-call-path 1818 compare found?, 0/false 1819 break-if-= $render-line:cache-cursor-column 1820 } 1821 var dest/edi: (addr int) <- copy cursor-row-addr 1822 { 1823 var src/eax: int <- copy top-row 1824 copy-to *dest, src 1825 } 1826 dest <- copy cursor-col-addr 1827 copy-to *dest, old-col 1828 var cursor-index-in-word/eax: int <- cursor-index curr-word 1829 add-to *dest, cursor-index-in-word 1830 } 1831 # loop update 1832 #? print-string 0, "next word\n" 1833 var next-word-ah/edx: (addr handle word) <- get curr-word, next 1834 curr-word <- lookup *next-word-ah 1835 #? { 1836 #? var foo/eax: int <- copy curr-word 1837 #? print-int32-hex 0, foo 1838 #? print-string 0, "\n" 1839 #? } 1840 increment-final-element curr-path 1841 loop 1842 } 1843 return curr-col 1844 } 1845 1846 fn callee functions: (addr handle function), word: (addr word), out: (addr handle function) { 1847 var stream-storage: (stream byte 0x10) 1848 var stream/esi: (addr stream byte) <- address stream-storage 1849 emit-word word, stream 1850 find-function functions, stream, out 1851 } 1852 1853 # Render: 1854 # - starting at top-row, left-col: final-word 1855 # - starting somewhere below at left-col: the stack result from interpreting first-world to final-word (inclusive) 1856 # 1857 # Return the farthest column written. 1858 fn render-column screen: (addr screen), functions: (addr handle function), bindings: (addr table), first-line: (addr line), line: (addr line), final-word: (addr word), top-row: int, left-col: int -> _/ecx: int { 1859 #? print-string 0, "render-column\n" 1860 #? dump-table bindings 1861 var max-width/esi: int <- copy 0 1862 { 1863 # compute stack 1864 var stack: value-stack 1865 var stack-addr/edi: (addr value-stack) <- address stack 1866 initialize-value-stack stack-addr, 0x10/max-words 1867 # copy bindings 1868 var bindings2-storage: table 1869 var bindings2/ebx: (addr table) <- address bindings2-storage 1870 #? print-string 0, "deep copy table {\n" 1871 deep-copy-table bindings, bindings2 1872 #? print-string 0, "}\n" 1873 evaluate functions, bindings2, first-line, final-word, stack-addr 1874 # indent stack 1875 var indented-col/ebx: int <- copy left-col 1876 indented-col <- add 1/margin-right 1877 # render stack 1878 var curr-row/edx: int <- copy top-row 1879 curr-row <- add 2/stack-margin-top 1880 { 1881 var top-addr/ecx: (addr int) <- get stack-addr, top 1882 compare *top-addr, 0 1883 break-if-<= 1884 decrement *top-addr 1885 var data-ah/eax: (addr handle array value) <- get stack-addr, data 1886 var data/eax: (addr array value) <- lookup *data-ah 1887 var top/ecx: int <- copy *top-addr 1888 var dest-offset/ecx: (offset value) <- compute-offset data, top 1889 var val/eax: (addr value) <- index data, dest-offset 1890 render-value-at screen, curr-row, indented-col, val, 1/top-level=true 1891 { 1892 var width/eax: int <- value-width val, 1 1893 compare width, max-width 1894 break-if-<= 1895 max-width <- copy width 1896 } 1897 var height/eax: int <- value-height val 1898 curr-row <- add height 1899 loop 1900 } 1901 } 1902 1903 max-width <- add 2 # spaces on either side of items on the stack 1904 1905 # render word, initialize result 1906 reset-formatting screen 1907 move-cursor screen, top-row, left-col 1908 print-word screen, final-word 1909 { 1910 var width/eax: int <- word-length final-word 1911 compare width, max-width 1912 break-if-<= 1913 max-width <- copy width 1914 } 1915 1916 # post-process right-col 1917 var right-col/ecx: int <- copy left-col 1918 right-col <- add max-width 1919 right-col <- add 1/margin-right 1920 #? print-int32-decimal 0, left-col 1921 #? print-string 0, " => " 1922 #? print-int32-decimal 0, right-col 1923 #? print-string 0, "\n" 1924 return right-col 1925 } 1926 1927 fn render-function-menu _env: (addr environment) { 1928 var env/esi: (addr environment) <- copy _env 1929 var screen-ah/edi: (addr handle screen) <- get env, screen 1930 var _screen/eax: (addr screen) <- lookup *screen-ah 1931 var screen/edi: (addr screen) <- copy _screen 1932 var nrows/eax: (addr int) <- get env, nrows 1933 move-cursor screen, *nrows, 0 1934 start-reverse-video screen 1935 print-string screen, " ctrl-q " 1936 reset-formatting screen 1937 print-string screen, " quit " 1938 start-reverse-video screen 1939 print-string screen, " ctrl-a " 1940 reset-formatting screen 1941 print-string screen, " ⏮ " 1942 start-reverse-video screen 1943 print-string screen, " ctrl-b " 1944 reset-formatting screen 1945 print-string screen, " ◀ word " 1946 start-reverse-video screen 1947 print-string screen, " ctrl-f " 1948 reset-formatting screen 1949 print-string screen, " word ▶ " 1950 start-reverse-video screen 1951 print-string screen, " ctrl-e " 1952 reset-formatting screen 1953 print-string screen, " ⏭ " 1954 start-reverse-video screen 1955 print-string screen, " ctrl-g " 1956 reset-formatting screen 1957 print-string screen, " go to function " 1958 start-reverse-video screen 1959 print-string screen, " tab " 1960 reset-formatting screen 1961 print-string screen, " go to sandbox" 1962 } 1963 1964 fn render-sandbox-menu _env: (addr environment) { 1965 var env/esi: (addr environment) <- copy _env 1966 var screen-ah/edi: (addr handle screen) <- get env, screen 1967 var _screen/eax: (addr screen) <- lookup *screen-ah 1968 var screen/edi: (addr screen) <- copy _screen 1969 var nrows/eax: (addr int) <- get env, nrows 1970 move-cursor screen, *nrows, 0 1971 start-reverse-video screen 1972 print-string screen, " ctrl-q " 1973 reset-formatting screen 1974 print-string screen, " quit " 1975 start-reverse-video screen 1976 print-string screen, " ctrl-a " 1977 reset-formatting screen 1978 print-string screen, " ⏮ " 1979 start-reverse-video screen 1980 print-string screen, " ctrl-b " 1981 reset-formatting screen 1982 print-string screen, " ◀ word " 1983 start-reverse-video screen 1984 print-string screen, " ctrl-f " 1985 reset-formatting screen 1986 print-string screen, " word ▶ " 1987 start-reverse-video screen 1988 print-string screen, " ctrl-e " 1989 reset-formatting screen 1990 print-string screen, " ⏭ " 1991 start-reverse-video screen 1992 print-string screen, " ctrl-g " 1993 reset-formatting screen 1994 print-string screen, " go to function " 1995 start-reverse-video screen 1996 print-string screen, " ctrl-l " 1997 reset-formatting screen 1998 print-string screen, " new line " 1999 start-reverse-video screen 2000 print-string screen, " ctrl-u " 2001 reset-formatting screen 2002 print-string screen, " clear " 2003 start-reverse-video screen 2004 print-string screen, " ctrl-n " 2005 reset-formatting screen 2006 print-string screen, " name word " 2007 start-reverse-video screen 2008 print-string screen, " ctrl-d " 2009 reset-formatting screen 2010 print-string screen, " define function" 2011 } 2012 2013 fn clear-canvas _env: (addr environment) { 2014 var env/esi: (addr environment) <- copy _env 2015 var screen-ah/edi: (addr handle screen) <- get env, screen 2016 var _screen/eax: (addr screen) <- lookup *screen-ah 2017 var screen/edi: (addr screen) <- copy _screen 2018 clear-screen screen 2019 var nrows/eax: (addr int) <- get env, nrows 2020 var sep-col/ecx: (addr int) <- get env, code-separator-col 2021 # divider 2022 draw-vertical-line screen, 1, *nrows, *sep-col 2023 # primitives 2024 var dummy/eax: int <- render-primitives screen, *nrows, *sep-col 2025 } 2026 2027 # return value: top-most row written to 2028 fn render-primitives screen: (addr screen), bottom-margin-row: int, right-col: int -> _/eax: int { 2029 # render primitives from the bottom of the screen upward 2030 var row/ecx: int <- copy bottom-margin-row 2031 row <- subtract 1 2032 var col/edx: int <- copy 1 2033 move-cursor screen, row, col 2034 row, col <- render-primitive-group screen, row, col, right-col, "numbers: ", "+ - * / sqrt " 2035 row, col <- render-primitive-group screen, row, col, right-col, "arrays: ", "len " 2036 row, col <- render-primitive-group screen, row, col, right-col, "files: ", "open read slurp lines " 2037 row, col <- render-primitive-group screen, row, col, right-col, "misc: ", "dup swap " # hack: keep these at the right of the bottom row 2038 row, col <- render-primitive-group screen, row, col, right-col, "screens: ", "fake-screen print move up down left right " 2039 # finally print heading up top 2040 row <- decrement 2041 move-cursor screen, row, 1 2042 start-bold screen 2043 print-string screen, "primitives:" 2044 reset-formatting screen 2045 return row 2046 } 2047 2048 # start at row, col and print the given strings 2049 # move up one row if there isn't enough room before right-col 2050 # return row, col printed until 2051 fn render-primitive-group screen: (addr screen), _row: int, _col: int, right-col: int, _heading: (addr array byte), _contents: (addr array byte) -> _/ecx: int, _/edx: int { 2052 var row/ecx: int <- copy _row 2053 var col/edx: int <- copy _col 2054 # decrement row if necessary 2055 var new-col/ebx: int <- copy col 2056 var heading/esi: (addr array byte) <- copy _heading 2057 var len1/eax: int <- length heading 2058 new-col <- add len1 2059 var contents/edi: (addr array byte) <- copy _contents 2060 var len2/eax: int <- length contents 2061 new-col <- add len2 2062 var bound/eax: int <- copy right-col 2063 bound <- decrement 2064 { 2065 compare new-col, bound 2066 break-if-<= 2067 row <- decrement 2068 col <- copy 1 2069 } 2070 move-cursor screen, row, col 2071 start-color screen, 0xf6, 7 2072 print-string screen, heading 2073 reset-formatting screen 2074 print-string screen, contents 2075 return row, new-col 2076 } 2077 2078 fn render-functions screen: (addr screen), right-col: int, _env: (addr environment) { 2079 var row/ecx: int <- copy 1 2080 var dummy-col/edx: int <- copy right-col 2081 var env/esi: (addr environment) <- copy _env 2082 var functions/esi: (addr handle function) <- get env, functions 2083 { 2084 var curr/eax: (addr function) <- lookup *functions 2085 compare curr, 0 2086 break-if-= 2087 row, dummy-col <- render-function-right-aligned screen, row, right-col, curr 2088 functions <- get curr, next 2089 row <- add 1/inter-function-margin 2090 loop 2091 } 2092 } 2093 2094 # print function starting at row, right-aligned before right-col 2095 # return row, col printed until 2096 fn render-function-right-aligned screen: (addr screen), row: int, right-col: int, f: (addr function) -> _/ecx: int, _/edx: int { 2097 var col/edx: int <- copy right-col 2098 col <- subtract 1/function-right-margin 2099 var col2/ebx: int <- copy col 2100 var width/eax: int <- function-width f 2101 col <- subtract width 2102 var new-row/ecx: int <- copy row 2103 var height/eax: int <- function-height f 2104 new-row <- add height 2105 new-row <- decrement 2106 col <- subtract 1/function-left-padding 2107 start-color screen, 0, 0xf7 2108 clear-rect screen, row, col, new-row, col2 2109 col <- add 1 2110 #? var dummy/eax: grapheme <- read-key-from-real-keyboard 2111 render-function screen, row, col, f 2112 new-row <- add 1/function-bottom-margin 2113 col <- subtract 1/function-left-padding 2114 col <- subtract 1/function-left-margin 2115 reset-formatting screen 2116 return new-row, col 2117 } 2118 2119 # render function starting at row, col 2120 # only single-line functions supported for now 2121 fn render-function screen: (addr screen), row: int, col: int, _f: (addr function) { 2122 var f/esi: (addr function) <- copy _f 2123 var args/ecx: (addr handle word) <- get f, args 2124 move-cursor screen, row, col 2125 print-words-in-reverse screen, args 2126 var name-ah/eax: (addr handle array byte) <- get f, name 2127 var name/eax: (addr array byte) <- lookup *name-ah 2128 start-bold screen 2129 print-string screen, name 2130 reset-formatting screen 2131 start-color screen, 0, 0xf7 2132 increment row 2133 add-to col, 2 2134 move-cursor screen, row, col 2135 print-string screen, "≡ " 2136 add-to col, 2 2137 var cursor-row/ecx: (addr int) <- get f, cursor-row 2138 var cursor-col/edx: (addr int) <- get f, cursor-col 2139 var cursor-word-ah/eax: (addr handle word) <- get f, cursor-word 2140 var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah 2141 var cursor-word/ebx: (addr word) <- copy _cursor-word 2142 var body-ah/eax: (addr handle line) <- get f, body 2143 var body/eax: (addr line) <- lookup *body-ah 2144 render-line-without-stack screen, body, row, col, cursor-word, cursor-row, cursor-col 2145 } 2146 2147 fn real-grapheme? g: grapheme -> _/eax: boolean { 2148 # if g == newline return true 2149 compare g, 0xa 2150 { 2151 break-if-!= 2152 return 1/true 2153 } 2154 # if g == tab return true 2155 compare g, 9 2156 { 2157 break-if-!= 2158 return 1/true 2159 } 2160 # if g < 32 return false 2161 compare g, 0x20 2162 { 2163 break-if->= 2164 return 0/false 2165 } 2166 # if g <= 255 return true 2167 compare g, 0xff 2168 { 2169 break-if-> 2170 return 1/true 2171 } 2172 # if (g&0xff == Esc) it's an escape sequence 2173 and-with g, 0xff 2174 compare g, 0x1b/esc 2175 { 2176 break-if-!= 2177 return 0/false 2178 } 2179 # otherwise return true 2180 return 1/true 2181 }