From d409be9b29b4bbfdf10fa26620fa6abbe45a87da Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 20 Oct 2020 23:06:24 -0700 Subject: 7088 --- html/apps/tile/box.mu.html | 194 ++-- html/apps/tile/data.mu.html | 842 ++++++++++++--- html/apps/tile/environment.mu.html | 1910 +++++++++++++++++++++++++-------- html/apps/tile/gap-buffer.mu.html | 28 + html/apps/tile/grapheme-stack.mu.html | 334 +++--- html/apps/tile/int-stack.mu.html | 23 - html/apps/tile/main.mu.html | 170 +-- html/apps/tile/rpn.mu.html | 387 ++++--- html/apps/tile/value-stack.mu.html | 11 +- html/apps/tile/word.mu.html | 591 ++++++---- 10 files changed, 3168 insertions(+), 1322 deletions(-) diff --git a/html/apps/tile/box.mu.html b/html/apps/tile/box.mu.html index 689dc7bf..40a28dd4 100644 --- a/html/apps/tile/box.mu.html +++ b/html/apps/tile/box.mu.html @@ -56,91 +56,115 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/apps/tile/box.mu
- 1 fn draw-box screen: (addr screen), row1: int, col1: int, row2: int, col2: int {
- 2   draw-horizontal-line screen, row1, col1, col2
- 3   draw-vertical-line screen, row1, row2, col1
- 4   draw-horizontal-line screen, row2, col1, col2
- 5   draw-vertical-line screen, row1, row2, col2
- 6 }
- 7 
- 8 fn draw-hatching screen: (addr screen), row1: int, col1: int, row2: int, col2: int {
- 9   var c/eax: int <- copy col1
-10   var r1/ecx: int <- copy row1
-11   r1 <- increment
-12   c <- add 2
-13   {
-14     compare c, col2
-15     break-if->=
-16     draw-vertical-line screen, r1, row2, c
-17     c <- add 2
-18     loop
-19   }
-20 }
-21 
-22 fn draw-horizontal-line screen: (addr screen), row: int, col1: int, col2: int {
-23   var col/eax: int <- copy col1
-24   move-cursor 0, row, col
-25   {
-26     compare col, col2
-27     break-if->=
-28     print-code-point screen, 0x2500
-29     col <- increment
-30     loop
-31   }
-32 }
-33 
-34 fn draw-vertical-line screen: (addr screen), row1: int, row2: int, col: int {
-35   var row/eax: int <- copy row1
-36   {
-37     compare row, row2
-38     break-if->=
-39     move-cursor 0, row, col
-40     print-code-point screen, 0x2502
-41     row <- increment
-42     loop
-43   }
-44 }
-45 
-46 # erase parts of screen the slow way
-47 fn clear-rect screen: (addr screen), row1: int, col1: int, row2: int, col2: int {
-48   var i/eax: int <- copy row1
-49   {
-50     compare i, row2
-51     break-if->
-52     var j/ecx: int <- copy col1
-53     move-cursor screen, i, j
-54     {
-55       compare j, col2
-56       break-if->
-57       print-grapheme screen 0x20  # space
-58       j <- increment
-59       loop
-60     }
-61     i <- increment
-62     loop
-63   }
-64 }
-65 
-66 fn clear-rect2 screen: (addr screen), row1: int, col1: int, w: int, h: int {
-67   var i/eax: int <- copy 0
-68   var curr-row/esi: int <- copy row1
-69   {
-70     compare i, w
-71     break-if->=
-72     move-cursor screen, curr-row, col1
-73     var j/ecx: int <- copy 0
-74     {
-75       compare j, h
-76       break-if->=
-77       print-grapheme screen 0x20  # space
-78       j <- increment
-79       loop
-80     }
-81     i <- increment
-82     curr-row <- increment
-83     loop
-84   }
-85 }
+  1 fn draw-box screen: (addr screen), row1: int, col1: int, row2: int, col2: int {
+  2   draw-horizontal-line screen, row1, col1, col2
+  3   draw-vertical-line screen, row1, row2, col1
+  4   draw-horizontal-line screen, row2, col1, col2
+  5   draw-vertical-line screen, row1, row2, col2
+  6   draw-top-left-corner screen, row1, col1
+  7   draw-top-right-corner screen, row1, col2
+  8   draw-bottom-left-corner screen, row2, col1
+  9   draw-bottom-right-corner screen, row2, col2
+ 10 }
+ 11 
+ 12 fn draw-hatching screen: (addr screen), row1: int, col1: int, row2: int, col2: int {
+ 13   var c/eax: int <- copy col1
+ 14   var r1/ecx: int <- copy row1
+ 15   r1 <- increment
+ 16   c <- add 2
+ 17   {
+ 18     compare c, col2
+ 19     break-if->=
+ 20     draw-vertical-line screen, r1, row2, c
+ 21     c <- add 2
+ 22     loop
+ 23   }
+ 24 }
+ 25 
+ 26 fn draw-horizontal-line screen: (addr screen), row: int, col1: int, col2: int {
+ 27   var col/eax: int <- copy col1
+ 28   move-cursor screen, row, col
+ 29   {
+ 30     compare col, col2
+ 31     break-if->=
+ 32     print-code-point screen, 0x2500
+ 33     col <- increment
+ 34     loop
+ 35   }
+ 36 }
+ 37 
+ 38 fn draw-vertical-line screen: (addr screen), row1: int, row2: int, col: int {
+ 39   var row/eax: int <- copy row1
+ 40   {
+ 41     compare row, row2
+ 42     break-if->=
+ 43     move-cursor screen, row, col
+ 44     print-code-point screen, 0x2502
+ 45     row <- increment
+ 46     loop
+ 47   }
+ 48 }
+ 49 
+ 50 fn draw-top-left-corner screen: (addr screen), row: int, col: int {
+ 51   move-cursor screen, row, col
+ 52   print-code-point screen, 0x250c
+ 53 }
+ 54 
+ 55 fn draw-top-right-corner screen: (addr screen), row: int, col: int {
+ 56   move-cursor screen, row, col
+ 57   print-code-point screen, 0x2510
+ 58 }
+ 59 
+ 60 fn draw-bottom-left-corner screen: (addr screen), row: int, col: int {
+ 61   move-cursor screen, row, col
+ 62   print-code-point screen, 0x2514
+ 63 }
+ 64 
+ 65 fn draw-bottom-right-corner screen: (addr screen), row: int, col: int {
+ 66   move-cursor screen, row, col
+ 67   print-code-point screen, 0x2518
+ 68 }
+ 69 
+ 70 # erase parts of screen the slow way
+ 71 fn clear-rect screen: (addr screen), row1: int, col1: int, row2: int, col2: int {
+ 72   var i/eax: int <- copy row1
+ 73   {
+ 74     compare i, row2
+ 75     break-if->
+ 76     var j/ecx: int <- copy col1
+ 77     move-cursor screen, i, j
+ 78     {
+ 79       compare j, col2
+ 80       break-if->
+ 81       print-grapheme screen 0x20  # space
+ 82       j <- increment
+ 83       loop
+ 84     }
+ 85     i <- increment
+ 86     loop
+ 87   }
+ 88 }
+ 89 
+ 90 fn clear-rect2 screen: (addr screen), row1: int, col1: int, w: int, h: int {
+ 91   var i/eax: int <- copy 0
+ 92   var curr-row/esi: int <- copy row1
+ 93   {
+ 94     compare i, w
+ 95     break-if->=
+ 96     move-cursor screen, curr-row, col1
+ 97     var j/ecx: int <- copy 0
+ 98     {
+ 99       compare j, h
+100       break-if->=
+101       print-grapheme screen 0x20  # space
+102       j <- increment
+103       loop
+104     }
+105     i <- increment
+106     curr-row <- increment
+107     loop
+108   }
+109 }
 
diff --git a/html/apps/tile/data.mu.html b/html/apps/tile/data.mu.html index 4e0c9f7d..6ead395a 100644 --- a/html/apps/tile/data.mu.html +++ b/html/apps/tile/data.mu.html @@ -14,6 +14,7 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.CommentedCode { color: #8a8a8a; } .muComment { color: #005faf; } .LineNr { } .SpecialChar { color: #d70000; } @@ -56,49 +57,49 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/apps/tile/data.mu
-  1 type program {
-  2   defs: (handle function)
-  3   sandboxes: (handle sandbox)
-  4 }
-  5 
-  6 type sandbox {
-  7   setup: (handle line)
-  8   data: (handle line)
-  9   next: (handle sandbox)
- 10   prev: (handle sandbox)
- 11 }
- 12 
- 13 type function {
- 14   name: (handle array byte)
- 15   args: (handle word)  # in reverse order
- 16   body: (handle line)
- 17   # some sort of indication of spatial location
- 18   next: (handle function)
- 19 }
- 20 
- 21 type line {
- 22   name: (handle array byte)
- 23   data: (handle word)
- 24   result: (handle result)  # might be cached
- 25   next: (handle line)
- 26   prev: (handle line)
- 27 }
- 28 
- 29 type word {
- 30   # at most one of these will be non-null
- 31   scalar-data: (handle gap-buffer)
- 32   text-data: (handle array byte)
- 33   box-data: (handle line)  # recurse
- 34   # other metadata attached to this word
- 35   display-subsidiary-stack?: boolean
- 36   next: (handle word)
- 37   prev: (handle word)
+  1 type sandbox {
+  2   setup: (handle line)
+  3   data: (handle line)
+  4   # display data
+  5   cursor-call-path: (handle call-path-element)
+  6   expanded-words: (handle call-path)
+  7   partial-name-for-cursor-word: (handle word)  # only when renaming word
+  8   partial-name-for-function: (handle word)  # only when defining function
+  9   #
+ 10   next: (handle sandbox)
+ 11   prev: (handle sandbox)
+ 12 }
+ 13 
+ 14 type function {
+ 15   name: (handle array byte)
+ 16   args: (handle word)  # in reverse order
+ 17   body: (handle line)
+ 18   # some sort of indication of spatial location
+ 19   next: (handle function)
+ 20 }
+ 21 
+ 22 type line {
+ 23   name: (handle array byte)
+ 24   data: (handle word)
+ 25   result: (handle result)  # might be cached
+ 26   next: (handle line)
+ 27   prev: (handle line)
+ 28 }
+ 29 
+ 30 type word {
+ 31   # at most one of these will be non-null
+ 32   scalar-data: (handle gap-buffer)
+ 33   text-data: (handle array byte)
+ 34   box-data: (handle line)  # recurse
+ 35   #
+ 36   next: (handle word)
+ 37   prev: (handle word)
  38 }
  39 
  40 type value {
  41   scalar-data: int
  42   text-data: (handle array byte)
- 43   box-data: (handle line)
+ 43   box-data: (handle line)
  44 }
  45 
  46 type table {
@@ -111,151 +112,642 @@ if ('onhashchange' in window) {
  53   value: (handle value)  # I'd inline this but we sometimes want to return a specific value from a table
  54 }
  55 
- 56 type result {
- 57   data: value-stack
- 58   error: (handle array byte)  # single error message for now
- 59 }
- 60 
- 61 # if 'out' is non-null, save the first word of the program there
- 62 fn initialize-program _program: (addr program), out: (addr handle word) {
- 63   var program/esi: (addr program) <- copy _program
- 64   var defs/eax: (addr handle function) <- get program, defs
- 65   create-primitive-defs defs
- 66   var sandbox-ah/eax: (addr handle sandbox) <- get program, sandboxes
- 67   allocate sandbox-ah
- 68   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
- 69   initialize-sandbox sandbox, out
- 70 }
- 71 
- 72 # if 'out' is non-null, save the first word of the sandbox there
- 73 fn initialize-sandbox _sandbox: (addr sandbox), out: (addr handle word) {
- 74   var sandbox/esi: (addr sandbox) <- copy _sandbox
- 75   var line-ah/eax: (addr handle line) <- get sandbox, data
- 76   allocate line-ah
- 77   var line/eax: (addr line) <- lookup *line-ah
- 78   initialize-line line, out
- 79 }
- 80 
- 81 # initialize line with a single empty word
- 82 # if 'out' is non-null, save the word there as well
- 83 fn initialize-line _line: (addr line), out: (addr handle word) {
- 84   var line/esi: (addr line) <- copy _line
- 85   var word-ah/eax: (addr handle word) <- get line, data
- 86   allocate word-ah
- 87   {
- 88     compare out, 0
- 89     break-if-=
- 90     var dest/edi: (addr handle word) <- copy out
- 91     copy-object word-ah, dest
- 92   }
- 93   var word/eax: (addr word) <- lookup *word-ah
- 94   initialize-word word
+ 56 # A call-path is a data structure that can unambiguously refer to any specific
+ 57 # call arbitrarily deep inside the call hierarchy of a program.
+ 58 type call-path {
+ 59   data: (handle call-path-element)
+ 60   next: (handle call-path)
+ 61 }
+ 62 
+ 63 # A call-path element is a list of elements, each of which corresponds to some call.
+ 64 type call-path-element {
+ 65   word: (handle word)
+ 66   next: (handle call-path-element)
+ 67 }
+ 68 
+ 69 type result {
+ 70   data: value-stack
+ 71   error: (handle array byte)  # single error message for now
+ 72 }
+ 73 
+ 74 fn initialize-sandbox _sandbox: (addr sandbox) {
+ 75   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 76   var line-ah/eax: (addr handle line) <- get sandbox, data
+ 77   allocate line-ah
+ 78   var line/eax: (addr line) <- lookup *line-ah
+ 79   initialize-line line
+ 80   var word-ah/ecx: (addr handle word) <- get line, data
+ 81   var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 82   allocate cursor-call-path-ah
+ 83   var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 84   var dest/eax: (addr handle word) <- get cursor-call-path, word
+ 85   copy-object word-ah, dest
+ 86 }
+ 87 
+ 88 # initialize line with a single empty word
+ 89 fn initialize-line _line: (addr line) {
+ 90   var line/esi: (addr line) <- copy _line
+ 91   var word-ah/eax: (addr handle word) <- get line, data
+ 92   allocate word-ah
+ 93   var word/eax: (addr word) <- lookup *word-ah
+ 94   initialize-word word
  95 }
  96 
- 97 fn create-primitive-defs _self: (addr handle function) {
+ 97 fn create-primitive-functions _self: (addr handle function) {
  98   # x 2* = x 2 *
- 99   var self/esi: (addr handle function) <- copy _self
+ 99   var self/esi: (addr handle function) <- copy _self
 100   allocate self
-101   var _f/eax: (addr function) <- lookup *self
-102   var f/esi: (addr function) <- copy _f
+101   var _f/eax: (addr function) <- lookup *self
+102   var f/esi: (addr function) <- copy _f
 103   var name-ah/eax: (addr handle array byte) <- get f, name
-104   populate-text-with name-ah, "2*"
-105   var args-ah/eax: (addr handle word) <- get f, args
+104   populate-text-with name-ah, "2*"
+105   var args-ah/eax: (addr handle word) <- get f, args
 106   allocate args-ah
-107   var args/eax: (addr word) <- lookup *args-ah
+107   var args/eax: (addr word) <- lookup *args-ah
 108   initialize-word-with args, "x"
-109   var body-ah/eax: (addr handle line) <- get f, body
+109   var body-ah/eax: (addr handle line) <- get f, body
 110   allocate body-ah
-111   var body/eax: (addr line) <- lookup *body-ah
-112   initialize-line body, 0
-113   var curr-word-ah/ecx: (addr handle word) <- get body, data
-114   allocate curr-word-ah
-115   var curr-word/eax: (addr word) <- lookup *curr-word-ah
-116   initialize-word-with curr-word, "x"
-117   curr-word-ah <- get curr-word, next
-118   allocate curr-word-ah
-119   curr-word <- lookup *curr-word-ah
-120   initialize-word-with curr-word, "2"
-121   curr-word-ah <- get curr-word, next
-122   allocate curr-word-ah
-123   curr-word <- lookup *curr-word-ah
-124   initialize-word-with curr-word, "*"
-125   # x 1+ = x 1 +
-126   var next/esi: (addr handle function) <- get f, next
-127   allocate next
-128   var _f/eax: (addr function) <- lookup *next
-129   var f/esi: (addr function) <- copy _f
-130   var name-ah/eax: (addr handle array byte) <- get f, name
-131   populate-text-with name-ah, "1+"
-132   var args-ah/eax: (addr handle word) <- get f, args
-133   allocate args-ah
-134   var args/eax: (addr word) <- lookup *args-ah
-135   initialize-word-with args, "x"
-136   var body-ah/eax: (addr handle line) <- get f, body
-137   allocate body-ah
-138   var body/eax: (addr line) <- lookup *body-ah
-139   initialize-line body, 0
-140   var curr-word-ah/ecx: (addr handle word) <- get body, data
-141   allocate curr-word-ah
-142   var curr-word/eax: (addr word) <- lookup *curr-word-ah
-143   initialize-word-with curr-word, "x"
-144   curr-word-ah <- get curr-word, next
-145   allocate curr-word-ah
-146   curr-word <- lookup *curr-word-ah
-147   initialize-word-with curr-word, "1"
-148   curr-word-ah <- get curr-word, next
-149   allocate curr-word-ah
-150   curr-word <- lookup *curr-word-ah
-151   initialize-word-with curr-word, "+"
-152   # x 2+ = x 1+ 1+
-153   var next/esi: (addr handle function) <- get f, next
-154   allocate next
-155   var _f/eax: (addr function) <- lookup *next
-156   var f/ecx: (addr function) <- copy _f
-157   var name-ah/eax: (addr handle array byte) <- get f, name
-158   populate-text-with name-ah, "2+"
-159   var args-ah/eax: (addr handle word) <- get f, args
-160   allocate args-ah
-161   var args/eax: (addr word) <- lookup *args-ah
-162   initialize-word-with args, "x"
-163   var body-ah/eax: (addr handle line) <- get f, body
-164   allocate body-ah
-165   var body/eax: (addr line) <- lookup *body-ah
-166   initialize-line body, 0
-167   var curr-word-ah/ecx: (addr handle word) <- get body, data
-168   allocate curr-word-ah
-169   var curr-word/eax: (addr word) <- lookup *curr-word-ah
-170   initialize-word-with curr-word, "x"
-171   curr-word-ah <- get curr-word, next
-172   allocate curr-word-ah
-173   curr-word <- lookup *curr-word-ah
-174   initialize-word-with curr-word, "1+"
-175   curr-word-ah <- get curr-word, next
-176   allocate curr-word-ah
-177   curr-word <- lookup *curr-word-ah
-178   initialize-word-with curr-word, "1+"
-179   # TODO: populate prev pointers
-180 }
-181 
-182 fn populate-text-with _out: (addr handle array byte), _in: (addr array byte) {
-183   var in/esi: (addr array byte) <- copy _in
-184   var n/ecx: int <- length in
-185   var out/edx: (addr handle array byte) <- copy _out
-186   populate out, n
-187   var _out-addr/eax: (addr array byte) <- lookup *out
-188   var out-addr/edx: (addr array byte) <- copy _out-addr
-189   var i/eax: int <- copy 0
-190   {
-191     compare i, n
-192     break-if->=
-193     var src/esi: (addr byte) <- index in, i
-194     var val/ecx: byte <- copy-byte *src
-195     var dest/edi: (addr byte) <- index out-addr, i
-196     copy-byte-to *dest, val
-197     i <- increment
-198     loop
-199   }
-200 }
+111   var body/eax: (addr line) <- lookup *body-ah
+112   initialize-line body
+113   var curr-word-ah/ecx: (addr handle word) <- get body, data
+114   # *curr-word = "x"
+115   allocate curr-word-ah
+116   var tmp/eax: (addr word) <- lookup *curr-word-ah
+117   var curr-word/edx: (addr word) <- copy tmp
+118   initialize-word-with curr-word, "x"
+119   # *curr-word->next = "2"
+120   var next-word-ah/ebx: (addr handle word) <- get curr-word, next
+121   allocate next-word-ah
+122   tmp <- lookup *next-word-ah
+123   initialize-word-with tmp, "2"
+124   # *curr-word->next->prev = curr-word
+125   var prev-word-ah/edi: (addr handle word) <- get tmp, prev
+126   copy-object curr-word-ah, prev-word-ah
+127   # curr-word = curr-word->next
+128   curr-word-ah <- copy next-word-ah
+129   curr-word <- copy tmp
+130   # *curr-word->next = "*"
+131   next-word-ah <- get curr-word, next
+132   allocate next-word-ah
+133   tmp <- lookup *next-word-ah
+134   initialize-word-with tmp, "*"
+135   # *curr-word->next->prev = curr-word
+136   prev-word-ah <- get tmp, prev
+137   copy-object curr-word-ah, prev-word-ah
+138   tmp <- lookup *prev-word-ah
+139   # x 1+ = x 1 +
+140   var next/esi: (addr handle function) <- get f, next
+141   allocate next
+142   var _f/eax: (addr function) <- lookup *next
+143   var f/esi: (addr function) <- copy _f
+144   var name-ah/eax: (addr handle array byte) <- get f, name
+145   populate-text-with name-ah, "1+"
+146   var args-ah/eax: (addr handle word) <- get f, args
+147   allocate args-ah
+148   var args/eax: (addr word) <- lookup *args-ah
+149   initialize-word-with args, "x"
+150   var body-ah/eax: (addr handle line) <- get f, body
+151   allocate body-ah
+152   var body/eax: (addr line) <- lookup *body-ah
+153   initialize-line body
+154   var curr-word-ah/ecx: (addr handle word) <- get body, data
+155   # *curr-word = "x"
+156   allocate curr-word-ah
+157   var tmp/eax: (addr word) <- lookup *curr-word-ah
+158   curr-word <- copy tmp
+159   initialize-word-with curr-word, "x"
+160   # *curr-word->next = "1"
+161   next-word-ah <- get curr-word, next
+162   allocate next-word-ah
+163   tmp <- lookup *next-word-ah
+164   initialize-word-with tmp, "1"
+165   # *curr-word->next->prev = curr-word
+166   prev-word-ah <- get tmp, prev
+167   copy-object curr-word-ah, prev-word-ah
+168   # curr-word = curr-word->next
+169   curr-word-ah <- copy next-word-ah
+170   curr-word <- copy tmp
+171   # *curr-word->next = "+"
+172   next-word-ah <- get curr-word, next
+173   allocate next-word-ah
+174   tmp <- lookup *next-word-ah
+175   initialize-word-with tmp, "+"
+176   # *curr-word->next->prev = curr-word
+177   prev-word-ah <- get tmp, prev
+178   copy-object curr-word-ah, prev-word-ah
+179   tmp <- lookup *prev-word-ah
+180   # x 2+ = x 1+ 1+
+181   var next/esi: (addr handle function) <- get f, next
+182   allocate next
+183   var _f/eax: (addr function) <- lookup *next
+184   var f/esi: (addr function) <- copy _f
+185   var name-ah/eax: (addr handle array byte) <- get f, name
+186   populate-text-with name-ah, "2+"
+187   var args-ah/eax: (addr handle word) <- get f, args
+188   allocate args-ah
+189   var args/eax: (addr word) <- lookup *args-ah
+190   initialize-word-with args, "x"
+191   var body-ah/eax: (addr handle line) <- get f, body
+192   allocate body-ah
+193   var body/eax: (addr line) <- lookup *body-ah
+194   initialize-line body
+195   var curr-word-ah/ecx: (addr handle word) <- get body, data
+196   # *curr-word = "x"
+197   allocate curr-word-ah
+198   var tmp/eax: (addr word) <- lookup *curr-word-ah
+199   curr-word <- copy tmp
+200   initialize-word-with curr-word, "x"
+201   # *curr-word->next = "1+"
+202   next-word-ah <- get curr-word, next
+203   allocate next-word-ah
+204   tmp <- lookup *next-word-ah
+205   initialize-word-with tmp, "1+"
+206   # *curr-word->next->prev = curr-word
+207   prev-word-ah <- get tmp, prev
+208   copy-object curr-word-ah, prev-word-ah
+209   # curr-word = curr-word->next
+210   curr-word-ah <- copy next-word-ah
+211   curr-word <- copy tmp
+212   # *curr-word->next = "1+"
+213   next-word-ah <- get curr-word, next
+214   allocate next-word-ah
+215   tmp <- lookup *next-word-ah
+216   initialize-word-with tmp, "1+"
+217   # *curr-word->next->prev = curr-word
+218   prev-word-ah <- get tmp, prev
+219   copy-object curr-word-ah, prev-word-ah
+220   tmp <- lookup *prev-word-ah
+221   # x square = x x *
+222   var next/esi: (addr handle function) <- get f, next
+223   allocate next
+224   var _f/eax: (addr function) <- lookup *next
+225   var f/esi: (addr function) <- copy _f
+226   var name-ah/eax: (addr handle array byte) <- get f, name
+227   populate-text-with name-ah, "square"
+228   var args-ah/eax: (addr handle word) <- get f, args
+229   allocate args-ah
+230   var args/eax: (addr word) <- lookup *args-ah
+231   initialize-word-with args, "x"
+232   var body-ah/eax: (addr handle line) <- get f, body
+233   allocate body-ah
+234   var body/eax: (addr line) <- lookup *body-ah
+235   initialize-line body
+236   var curr-word-ah/ecx: (addr handle word) <- get body, data
+237   # *curr-word = "x"
+238   allocate curr-word-ah
+239   var tmp/eax: (addr word) <- lookup *curr-word-ah
+240   var curr-word/edx: (addr word) <- copy tmp
+241   initialize-word-with curr-word, "x"
+242   # *curr-word->next = "x"
+243   var next-word-ah/ebx: (addr handle word) <- get curr-word, next
+244   allocate next-word-ah
+245   tmp <- lookup *next-word-ah
+246   initialize-word-with tmp, "x"
+247   # *curr-word->next->prev = curr-word
+248   var prev-word-ah/edi: (addr handle word) <- get tmp, prev
+249   copy-object curr-word-ah, prev-word-ah
+250   # curr-word = curr-word->next
+251   curr-word-ah <- copy next-word-ah
+252   curr-word <- copy tmp
+253   # *curr-word->next = "*"
+254   next-word-ah <- get curr-word, next
+255   allocate next-word-ah
+256   tmp <- lookup *next-word-ah
+257   initialize-word-with tmp, "*"
+258   # *curr-word->next->prev = curr-word
+259   prev-word-ah <- get tmp, prev
+260   copy-object curr-word-ah, prev-word-ah
+261   tmp <- lookup *prev-word-ah
+262   # x 1- = x 1 -
+263   var next/esi: (addr handle function) <- get f, next
+264   allocate next
+265   var _f/eax: (addr function) <- lookup *next
+266   var f/esi: (addr function) <- copy _f
+267   var name-ah/eax: (addr handle array byte) <- get f, name
+268   populate-text-with name-ah, "1-"
+269   var args-ah/eax: (addr handle word) <- get f, args
+270   allocate args-ah
+271   var args/eax: (addr word) <- lookup *args-ah
+272   initialize-word-with args, "x"
+273   var body-ah/eax: (addr handle line) <- get f, body
+274   allocate body-ah
+275   var body/eax: (addr line) <- lookup *body-ah
+276   initialize-line body
+277   var curr-word-ah/ecx: (addr handle word) <- get body, data
+278   # *curr-word = "x"
+279   allocate curr-word-ah
+280   var tmp/eax: (addr word) <- lookup *curr-word-ah
+281   curr-word <- copy tmp
+282   initialize-word-with curr-word, "x"
+283   # *curr-word->next = "1"
+284   next-word-ah <- get curr-word, next
+285   allocate next-word-ah
+286   tmp <- lookup *next-word-ah
+287   initialize-word-with tmp, "1"
+288   # *curr-word->next->prev = curr-word
+289   prev-word-ah <- get tmp, prev
+290   copy-object curr-word-ah, prev-word-ah
+291   # curr-word = curr-word->next
+292   curr-word-ah <- copy next-word-ah
+293   curr-word <- copy tmp
+294   # *curr-word->next = "-"
+295   next-word-ah <- get curr-word, next
+296   allocate next-word-ah
+297   tmp <- lookup *next-word-ah
+298   initialize-word-with tmp, "-"
+299   # *curr-word->next->prev = curr-word
+300   prev-word-ah <- get tmp, prev
+301   copy-object curr-word-ah, prev-word-ah
+302   tmp <- lookup *prev-word-ah
+303   # x y sub = x y -
+304   var next/esi: (addr handle function) <- get f, next
+305   allocate next
+306   var _f/eax: (addr function) <- lookup *next
+307   var f/esi: (addr function) <- copy _f
+308   var name-ah/eax: (addr handle array byte) <- get f, name
+309   populate-text-with name-ah, "sub"
+310   # critical lesson: args are stored in reverse order
+311   var args-ah/eax: (addr handle word) <- get f, args
+312   allocate args-ah
+313   var args/eax: (addr word) <- lookup *args-ah
+314   initialize-word-with args, "y"
+315   var next-arg-ah/eax: (addr handle word) <- get args, next
+316   allocate next-arg-ah
+317   var next-arg/eax: (addr word) <- lookup *next-arg-ah
+318   initialize-word-with next-arg, "x"
+319   var body-ah/eax: (addr handle line) <- get f, body
+320   allocate body-ah
+321   var body/eax: (addr line) <- lookup *body-ah
+322   initialize-line body
+323   var curr-word-ah/ecx: (addr handle word) <- get body, data
+324   # *curr-word = "x"
+325   allocate curr-word-ah
+326   var tmp/eax: (addr word) <- lookup *curr-word-ah
+327   curr-word <- copy tmp
+328   initialize-word-with curr-word, "x"
+329   # *curr-word->next = "y"
+330   next-word-ah <- get curr-word, next
+331   allocate next-word-ah
+332   tmp <- lookup *next-word-ah
+333   initialize-word-with tmp, "y"
+334   # *curr-word->next->prev = curr-word
+335   prev-word-ah <- get tmp, prev
+336   copy-object curr-word-ah, prev-word-ah
+337   # curr-word = curr-word->next
+338   curr-word-ah <- copy next-word-ah
+339   curr-word <- copy tmp
+340   # *curr-word->next = "-"
+341   next-word-ah <- get curr-word, next
+342   allocate next-word-ah
+343   tmp <- lookup *next-word-ah
+344   initialize-word-with tmp, "-"
+345   # *curr-word->next->prev = curr-word
+346   prev-word-ah <- get tmp, prev
+347   copy-object curr-word-ah, prev-word-ah
+348   tmp <- lookup *prev-word-ah
+349 }
+350 
+351 fn function-body functions: (addr handle function), _word: (addr handle word), out: (addr handle line) {
+352   var function-name-storage: (handle array byte)
+353   var function-name-ah/ecx: (addr handle array byte) <- address function-name-storage
+354   var word-ah/esi: (addr handle word) <- copy _word
+355   var word/eax: (addr word) <- lookup *word-ah
+356   var gap-ah/eax: (addr handle gap-buffer) <- get word, scalar-data
+357   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
+358   gap-buffer-to-string gap, function-name-ah
+359   var _function-name/eax: (addr array byte) <- lookup *function-name-ah
+360   var function-name/esi: (addr array byte) <- copy _function-name
+361   var curr-ah/ecx: (addr handle function) <- copy functions
+362   $function-body:loop: {
+363     var _curr/eax: (addr function) <- lookup *curr-ah
+364     var curr/edx: (addr function) <- copy _curr
+365     compare curr, 0
+366     break-if-=
+367     var curr-name-ah/eax: (addr handle array byte) <- get curr, name
+368     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
+369     var found?/eax: boolean <- string-equal? curr-name, function-name
+370     compare found?, 0  # false
+371     {
+372       break-if-=
+373       var src/eax: (addr handle line) <- get curr, body
+374       copy-object src, out
+375       break $function-body:loop
+376     }
+377     curr-ah <- get curr, next
+378     loop
+379   }
+380 }
+381 
+382 fn body-length functions: (addr handle function), function-name: (addr handle word) -> result/eax: int {
+383   var body-storage: (handle line)
+384   var body-ah/edi: (addr handle line) <- address body-storage
+385   function-body functions, function-name, body-ah
+386   var body/eax: (addr line) <- lookup *body-ah
+387   result <- line-length body
+388 }
+389 
+390 fn line-length _in: (addr line) -> result/eax: int {
+391   var in/esi: (addr line) <- copy _in
+392   var curr-ah/ecx: (addr handle word) <- get in, data
+393   var out/edi: int <- copy 0
+394   {
+395     var curr/eax: (addr word) <- lookup *curr-ah
+396     compare curr, 0
+397     break-if-=
+398     curr-ah <- get curr, next
+399     out <- increment
+400     loop
+401   }
+402   result <- copy out
+403 }
+404 
+405 fn populate-text-with _out: (addr handle array byte), _in: (addr array byte) {
+406   var in/esi: (addr array byte) <- copy _in
+407   var n/ecx: int <- length in
+408   var out/edx: (addr handle array byte) <- copy _out
+409   populate out, n
+410   var _out-addr/eax: (addr array byte) <- lookup *out
+411   var out-addr/edx: (addr array byte) <- copy _out-addr
+412   var i/eax: int <- copy 0
+413   {
+414     compare i, n
+415     break-if->=
+416     var src/esi: (addr byte) <- index in, i
+417     var val/ecx: byte <- copy-byte *src
+418     var dest/edi: (addr byte) <- index out-addr, i
+419     copy-byte-to *dest, val
+420     i <- increment
+421     loop
+422   }
+423 }
+424 
+425 fn initialize-path-from-sandbox _in: (addr sandbox), _out: (addr handle call-path-element) {
+426   var sandbox/esi: (addr sandbox) <- copy _in
+427   var line-ah/eax: (addr handle line) <- get sandbox, data
+428   var line/eax: (addr line) <- lookup *line-ah
+429   var src/esi: (addr handle word) <- get line, data
+430   var out-ah/edi: (addr handle call-path-element) <- copy _out
+431   var out/eax: (addr call-path-element) <- lookup *out-ah
+432   var dest/edi: (addr handle word) <- get out, word
+433   copy-object src, dest
+434 }
+435 
+436 fn initialize-path-from-line _line: (addr line), _out: (addr handle call-path-element) {
+437   var line/eax: (addr line) <- copy _line
+438   var src/esi: (addr handle word) <- get line, data
+439   var out-ah/edi: (addr handle call-path-element) <- copy _out
+440   var out/eax: (addr call-path-element) <- lookup *out-ah
+441   var dest/edi: (addr handle word) <- get out, word
+442   copy-object src, dest
+443 }
+444 
+445 fn find-in-call-paths call-paths: (addr handle call-path), needle: (addr handle call-path-element) -> result/eax: boolean {
+446   var curr-ah/esi: (addr handle call-path) <- copy call-paths
+447   var out/edi: boolean <- copy 0  # false
+448   $find-in-call-path:loop: {
+449     var curr/eax: (addr call-path) <- lookup *curr-ah
+450     compare curr, 0
+451     break-if-=
+452     {
+453       var curr-data/eax: (addr handle call-path-element) <- get curr, data
+454       var match?/eax: boolean <- call-path-element-match? curr-data, needle
+455       compare match?, 0  # false
+456       {
+457         break-if-=
+458         out <- copy 1  # true
+459         break $find-in-call-path:loop
+460       }
+461     }
+462     curr-ah <- get curr, next
+463     loop
+464   }
+465   result <- copy out
+466 }
+467 
+468 fn call-path-element-match? _x: (addr handle call-path-element), _y: (addr handle call-path-element) -> result/eax: boolean {
+469 $call-path-element-match?:body: {
+470   var x-ah/eax: (addr handle call-path-element) <- copy _x
+471   var x-a/eax: (addr call-path-element) <- lookup *x-ah
+472   var x/esi: (addr call-path-element) <- copy x-a
+473   var y-ah/eax: (addr handle call-path-element) <- copy _y
+474   var y-a/eax: (addr call-path-element) <- lookup *y-ah
+475   var y/edi: (addr call-path-element) <- copy y-a
+476   compare x, y
+477   {
+478     break-if-!=
+479     result <- copy 1  # true
+480     break $call-path-element-match?:body
+481   }
+482   compare x, 0
+483   {
+484     break-if-!=
+485     result <- copy 0  # false
+486     break $call-path-element-match?:body
+487   }
+488   compare y, 0
+489   {
+490     break-if-!=
+491     result <- copy 0  # false
+492     break $call-path-element-match?:body
+493   }
+494   # compare word addresses, not contents
+495   var x-data-ah/ecx: (addr handle word) <- get x, word
+496   var x-data-a/eax: (addr word) <- lookup *x-data-ah
+497   var x-data/ecx: int <- copy x-data-a
+498   var y-data-ah/eax: (addr handle word) <- get y, word
+499   var y-data-a/eax: (addr word) <- lookup *y-data-ah
+500   var y-data/eax: int <- copy y-data-a
+501 #?   print-string 0, "match? "
+502 #?   print-int32-hex 0, x-data
+503 #?   print-string 0, " vs "
+504 #?   print-int32-hex 0, y-data
+505 #?   print-string 0, "\n"
+506   compare x-data, y-data
+507   {
+508     break-if-=
+509     result <- copy 0  # false
+510     break $call-path-element-match?:body
+511   }
+512   var x-next/ecx: (addr handle call-path-element) <- get x, next
+513   var y-next/eax: (addr handle call-path-element) <- get y, next
+514   result <- call-path-element-match? x-next, y-next
+515 }
+516 }
+517 
+518 # order is irrelevant
+519 fn insert-in-call-path list: (addr handle call-path), new: (addr handle call-path-element) {
+520   var new-path-storage: (handle call-path)
+521   var new-path-ah/edi: (addr handle call-path) <- address new-path-storage
+522   allocate new-path-ah
+523   var new-path/eax: (addr call-path) <- lookup *new-path-ah
+524   var next/ecx: (addr handle call-path) <- get new-path, next
+525   copy-object list, next
+526   var dest/ecx: (addr handle call-path-element) <- get new-path, data
+527   deep-copy-call-path-element new, dest
+528   copy-object new-path-ah, list
+529 }
+530 
+531 # assumes dest is initially clear
+532 fn deep-copy-call-path-element _src: (addr handle call-path-element), _dest: (addr handle call-path-element) {
+533   var src/esi: (addr handle call-path-element) <- copy _src
+534   # if src is null, return
+535   var _src-addr/eax: (addr call-path-element) <- lookup *src
+536   compare _src-addr, 0
+537   break-if-=
+538   # allocate
+539   var src-addr/esi: (addr call-path-element) <- copy _src-addr
+540   var dest/eax: (addr handle call-path-element) <- copy _dest
+541   allocate dest
+542   # copy data
+543   var dest-addr/eax: (addr call-path-element) <- lookup *dest
+544   {
+545     var dest-data-addr/ecx: (addr handle word) <- get dest-addr, word
+546     var src-data-addr/eax: (addr handle word) <- get src-addr, word
+547     copy-object src-data-addr, dest-data-addr
+548   }
+549   # recurse
+550   var src-next/esi: (addr handle call-path-element) <- get src-addr, next
+551   var dest-next/eax: (addr handle call-path-element) <- get dest-addr, next
+552   deep-copy-call-path-element src-next, dest-next
+553 }
+554 
+555 fn delete-in-call-path list: (addr handle call-path), needle: (addr handle call-path-element) {
+556 $delete-in-call-path:body: {
+557   var curr-ah/esi: (addr handle call-path) <- copy list
+558   $delete-in-call-path:loop: {
+559     var _curr/eax: (addr call-path) <- lookup *curr-ah
+560     var curr/ecx: (addr call-path) <- copy _curr
+561     compare curr, 0
+562     break-if-=
+563     {
+564       var curr-data/eax: (addr handle call-path-element) <- get curr, data
+565       var match?/eax: boolean <- call-path-element-match? curr-data, needle
+566       compare match?, 0  # false
+567       {
+568         break-if-=
+569         var next-ah/ecx: (addr handle call-path) <- get curr, next
+570         copy-object next-ah, curr-ah
+571         loop $delete-in-call-path:loop
+572       }
+573     }
+574     curr-ah <- get curr, next
+575     loop
+576   }
+577 }
+578 }
+579 
+580 fn increment-final-element list: (addr handle call-path-element) {
+581   var final-ah/eax: (addr handle call-path-element) <- copy list
+582   var final/eax: (addr call-path-element) <- lookup *final-ah
+583   var val-ah/ecx: (addr handle word) <- get final, word
+584   var val/eax: (addr word) <- lookup *val-ah
+585   var new-ah/edx: (addr handle word) <- get val, next
+586   var target/eax: (addr word) <- lookup *new-ah
+587   compare target, 0
+588   break-if-=
+589   copy-object new-ah, val-ah
+590 }
+591 
+592 fn decrement-final-element list: (addr handle call-path-element) {
+593   var final-ah/eax: (addr handle call-path-element) <- copy list
+594   var final/eax: (addr call-path-element) <- lookup *final-ah
+595   var val-ah/ecx: (addr handle word) <- get final, word
+596   var val/eax: (addr word) <- lookup *val-ah
+597   var new-ah/edx: (addr handle word) <- get val, prev
+598   var target/eax: (addr word) <- lookup *new-ah
+599   compare target, 0
+600   break-if-=
+601   copy-object new-ah, val-ah
+602 }
+603 
+604 fn move-final-element-to-start-of-line list: (addr handle call-path-element) {
+605   var final-ah/eax: (addr handle call-path-element) <- copy list
+606   var final/eax: (addr call-path-element) <- lookup *final-ah
+607   var val-ah/ecx: (addr handle word) <- get final, word
+608   var val/eax: (addr word) <- lookup *val-ah
+609   var new-ah/edx: (addr handle word) <- get val, prev
+610   var target/eax: (addr word) <- lookup *new-ah
+611   compare target, 0
+612   break-if-=
+613   copy-object new-ah, val-ah
+614   move-final-element-to-start-of-line list
+615 }
+616 
+617 fn push-to-call-path-element list: (addr handle call-path-element), new: (addr handle word) {
+618   var new-element-storage: (handle call-path-element)
+619   var new-element-ah/edi: (addr handle call-path-element) <- address new-element-storage
+620   allocate new-element-ah
+621   var new-element/eax: (addr call-path-element) <- lookup *new-element-ah
+622   # save word
+623   var dest/ecx: (addr handle word) <- get new-element, word
+624   copy-object new, dest
+625   # save next
+626   var dest2/ecx: (addr handle call-path-element) <- get new-element, next
+627   copy-object list, dest2
+628   # return
+629   copy-object new-element-ah, list
+630 }
+631 
+632 fn drop-from-call-path-element _list: (addr handle call-path-element) {
+633   var list-ah/esi: (addr handle call-path-element) <- copy _list
+634   var list/eax: (addr call-path-element) <- lookup *list-ah
+635   var next/eax: (addr handle call-path-element) <- get list, next
+636   copy-object next, _list
+637 }
+638 
+639 fn drop-nested-calls _list: (addr handle call-path-element) {
+640   var list-ah/esi: (addr handle call-path-element) <- copy _list
+641   var list/eax: (addr call-path-element) <- lookup *list-ah
+642   var next-ah/edi: (addr handle call-path-element) <- get list, next
+643   var next/eax: (addr call-path-element) <- lookup *next-ah
+644   compare next, 0
+645   break-if-=
+646   copy-object next-ah, _list
+647   drop-nested-calls _list
+648 }
+649 
+650 fn dump-call-path-element screen: (addr screen), _x-ah: (addr handle call-path-element) {
+651 $dump-call-path-element:body: {
+652   var x-ah/ecx: (addr handle call-path-element) <- copy _x-ah
+653   var _x/eax: (addr call-path-element) <- lookup *x-ah
+654   var x/esi: (addr call-path-element) <- copy _x
+655   var word-ah/eax: (addr handle word) <- get x, word
+656   var word/eax: (addr word) <- lookup *word-ah
+657   print-word screen, word
+658   var next-ah/ecx: (addr handle call-path-element) <- get x, next
+659   var next/eax: (addr call-path-element) <- lookup *next-ah
+660   compare next, 0
+661   {
+662     break-if-=
+663     print-string screen, " "
+664     dump-call-path-element screen, next-ah
+665     break $dump-call-path-element:body
+666   }
+667   {
+668     break-if-!=
+669     print-string screen, "\n"
+670   }
+671 }
+672 }
+673 
+674 fn dump-call-paths screen: (addr screen), _x-ah: (addr handle call-path) {
+675 $dump-call-paths:body: {
+676   var x-ah/ecx: (addr handle call-path) <- copy _x-ah
+677   var x/eax: (addr call-path) <- lookup *x-ah
+678   compare x, 0
+679   break-if-=
+680   var src/ecx: (addr handle call-path-element) <- get x, data
+681   dump-call-path-element screen, src
+682   var next-ah/ecx: (addr handle call-path) <- get x, next
+683   var next/eax: (addr call-path) <- lookup *next-ah
+684   compare next, 0
+685   {
+686     break-if-=
+687     dump-call-paths screen, next-ah
+688     break $dump-call-paths:body
+689   }
+690 }
+691 }
 
diff --git a/html/apps/tile/environment.mu.html b/html/apps/tile/environment.mu.html index 2b375d76..216aeb5f 100644 --- a/html/apps/tile/environment.mu.html +++ b/html/apps/tile/environment.mu.html @@ -14,6 +14,7 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.CommentedCode { color: #8a8a8a; } .muComment { color: #005faf; } .LineNr { } .SpecialChar { color: #d70000; } @@ -21,6 +22,7 @@ a { color:inherit; } .muFunction { color: #af5f00; text-decoration: underline; } .Constant { color: #008787; } .PreProc { color: #c000c0; } +.muS2Comment { color: #8a8a8a; } --> @@ -56,465 +58,1455 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/apps/tile/environment.mu
-  1 type environment {
-  2   screen: (handle screen)
-  3   program: (handle program)
-  4   cursor-word: (handle word)
-  5   nrows: int
-  6   ncols: int
-  7   code-separator-col: int
-  8 }
-  9 
- 10 fn initialize-environment _env: (addr environment) {
- 11   var env/esi: (addr environment) <- copy _env
- 12   var program-ah/eax: (addr handle program) <- get env, program
- 13   allocate program-ah
- 14   var program/eax: (addr program) <- lookup *program-ah
- 15   var cursor-word-ah/ecx: (addr handle word) <- get env, cursor-word
- 16   initialize-program program, cursor-word-ah
- 17   # initialize screen
- 18   var screen-ah/eax: (addr handle screen) <- get env, screen
- 19   var _screen/eax: (addr screen) <- lookup *screen-ah
- 20   var screen/edi: (addr screen) <- copy _screen
- 21   var nrows/eax: int <- copy 0
- 22   var ncols/ecx: int <- copy 0
- 23   nrows, ncols <- screen-size screen
- 24   var dest/edx: (addr int) <- get env, nrows
- 25   copy-to *dest, nrows
- 26   dest <- get env, ncols
- 27   copy-to *dest, ncols
- 28   var repl-col/ecx: int <- copy ncols
- 29   repl-col <- shift-right 1
- 30   dest <- get env, code-separator-col
- 31   copy-to *dest, repl-col
- 32 }
- 33 
- 34 fn draw-screen _env: (addr environment) {
- 35   var env/esi: (addr environment) <- copy _env
- 36   var screen-ah/eax: (addr handle screen) <- get env, screen
- 37   var _screen/eax: (addr screen) <- lookup *screen-ah
- 38   var screen/edi: (addr screen) <- copy _screen
- 39   var dest/edx: (addr int) <- get env, code-separator-col
- 40   var tmp/eax: int <- copy *dest
- 41   clear-canvas env
- 42   tmp <- add 2  # repl-margin-left
- 43   move-cursor screen, 3, tmp  # input-row
- 44 }
- 45 
- 46 fn initialize-environment-with-fake-screen _self: (addr environment), nrows: int, ncols: int {
- 47   var self/esi: (addr environment) <- copy _self
- 48   var screen-ah/eax: (addr handle screen) <- get self, screen
- 49   allocate screen-ah
- 50   var screen-addr/eax: (addr screen) <- lookup *screen-ah
- 51   initialize-screen screen-addr, nrows, ncols
- 52   initialize-environment self
- 53 }
- 54 
- 55 fn process _self: (addr environment), key: grapheme {
- 56 $process:body: {
- 57     var self/esi: (addr environment) <- copy _self
- 58     compare key, 0x445b1b  # left-arrow
- 59     {
- 60       break-if-!=
- 61       var cursor-word-ah/edi: (addr handle word) <- get self, cursor-word
- 62       var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
- 63       var cursor-word/ecx: (addr word) <- copy _cursor-word
- 64       # if not at start, move left within current word
- 65       var at-start?/eax: boolean <- cursor-at-start? cursor-word
- 66       compare at-start?, 0  # false
- 67       {
- 68         break-if-=
- 69         cursor-left cursor-word
- 70         break $process:body
- 71       }
- 72       # otherwise, move to end of prev word
- 73       var prev-word-ah/esi: (addr handle word) <- get cursor-word, prev
- 74       var prev-word/eax: (addr word) <- lookup *prev-word-ah
- 75       {
- 76         compare prev-word, 0
- 77         break-if-=
- 78         copy-object prev-word-ah, cursor-word-ah
- 79         cursor-to-end prev-word
- 80       }
- 81       break $process:body
- 82     }
- 83     compare key, 0x435b1b  # right-arrow
- 84     {
- 85       break-if-!=
- 86       var cursor-word-ah/edi: (addr handle word) <- get self, cursor-word
- 87       var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
- 88       var cursor-word/ecx: (addr word) <- copy _cursor-word
- 89       # if not at end, move right within current word
- 90       var at-end?/eax: boolean <- cursor-at-end? cursor-word
- 91       compare at-end?, 0  # false
- 92       {
- 93         break-if-=
- 94         cursor-right cursor-word
- 95         break $process:body
- 96       }
- 97       # otherwise, move to start of next word
- 98       var next-word-ah/esi: (addr handle word) <- get cursor-word, next
- 99       var next-word/eax: (addr word) <- lookup *next-word-ah
-100       {
-101         compare next-word, 0
-102         break-if-=
-103         copy-object next-word-ah, cursor-word-ah
-104         cursor-to-start next-word
-105       }
-106       break $process:body
-107     }
-108     compare key, 0x7f  # del (backspace on Macs)
-109     {
-110       break-if-!=
-111       var cursor-word-ah/edi: (addr handle word) <- get self, cursor-word
-112       var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
-113       var cursor-word/ecx: (addr word) <- copy _cursor-word
-114       # if not at start of some word, delete grapheme before cursor within current word
-115       var at-start?/eax: boolean <- cursor-at-start? cursor-word
-116       compare at-start?, 0  # false
-117       {
-118         break-if-=
-119         delete-before-cursor cursor-word
-120         break $process:body
-121       }
-122       # otherwise delete current word and move to end of prev word
-123       var prev-word-ah/esi: (addr handle word) <- get cursor-word, prev
-124       var prev-word/eax: (addr word) <- lookup *prev-word-ah
-125       {
-126         compare prev-word, 0
-127         break-if-=
-128         copy-object prev-word-ah, cursor-word-ah
-129         cursor-to-end prev-word
-130         delete-next prev-word
-131       }
-132       break $process:body
-133     }
-134     compare key, 0x20  # space
-135     {
-136       break-if-!=
-137       # insert new word
-138       var cursor-word-ah/edx: (addr handle word) <- get self, cursor-word
-139       append-word cursor-word-ah
-140       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
-141       var next-word-ah/ecx: (addr handle word) <- get cursor-word, next
-142       copy-object next-word-ah, cursor-word-ah
-143       break $process:body
-144     }
-145     compare key, 0xa  # enter
-146     {
-147       break-if-!=
-148       # toggle display of subsidiary stack
-149       var cursor-word-ah/edx: (addr handle word) <- get self, cursor-word
-150       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
-151       var display-subsidiary-stack?/eax: (addr boolean) <- get cursor-word, display-subsidiary-stack?
-152       var tmp/ecx: int <- copy 1
-153       tmp <- subtract *display-subsidiary-stack?
-154       copy-to *display-subsidiary-stack?, tmp
-155       break $process:body
-156     }
-157     # otherwise insert key within current word
-158     var g/edx: grapheme <- copy key
-159     var print?/eax: boolean <- real-grapheme? key
-160     {
-161       compare print?, 0  # false
-162       break-if-=
-163       var cursor-word-ah/eax: (addr handle word) <- get self, cursor-word
-164       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
-165       add-grapheme-to-word cursor-word, g
-166       break $process:body
-167     }
-168     # silently ignore other hotkeys
-169 }
-170 }
-171 
-172 fn evaluate-environment _env: (addr environment), stack: (addr value-stack) {
-173   var env/esi: (addr environment) <- copy _env
-174   # program
-175   var program-ah/eax: (addr handle program) <- get env, program
-176   var _program/eax: (addr program) <- lookup *program-ah
-177   var program/esi: (addr program) <- copy _program
-178   # defs
-179   var defs/edx: (addr handle function) <- get program, defs
-180   # line
-181   var sandbox-ah/esi: (addr handle sandbox) <- get program, sandboxes
-182   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
-183   var line-ah/eax: (addr handle line) <- get sandbox, data
-184   var _line/eax: (addr line) <- lookup *line-ah
-185   var line/esi: (addr line) <- copy _line
-186   evaluate defs, 0, line, 0, stack
-187 }
-188 
-189 fn render _env: (addr environment) {
-190   var env/esi: (addr environment) <- copy _env
-191   clear-canvas env
-192   var screen-ah/edi: (addr handle screen) <- get env, screen
-193   var _screen/eax: (addr screen) <- lookup *screen-ah
-194   var screen/edi: (addr screen) <- copy _screen
-195   var _repl-col/ecx: (addr int) <- get env, code-separator-col
-196   var repl-col/ecx: int <- copy *_repl-col
-197   repl-col <- add 2  # repl-margin-left
-198   # cursor-word
-199   var cursor-word-ah/ebx: (addr handle word) <- get env, cursor-word
-200   var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
-201   var cursor-word/ebx: (addr word) <- copy _cursor-word
-202   # program
-203   var program-ah/eax: (addr handle program) <- get env, program
-204   var _program/eax: (addr program) <- lookup *program-ah
-205   var program/esi: (addr program) <- copy _program
-206   # defs
-207   var defs/edx: (addr handle function) <- get program, defs
-208   # line
-209   var sandbox-ah/esi: (addr handle sandbox) <- get program, sandboxes
-210   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
-211   var line-ah/eax: (addr handle line) <- get sandbox, data
-212   var _line/eax: (addr line) <- lookup *line-ah
-213   var line/esi: (addr line) <- copy _line
-214   # cursor-col
-215   var cursor-col: int
-216   var cursor-col-a/eax: (addr int) <- address cursor-col
-217   #
-218   var dummy/ecx: int <- render-line screen, defs, 0, line, 3, repl-col, cursor-word, cursor-col-a  # input-row=3
-219   move-cursor screen, 3, cursor-col  # input-row
-220 }
-221 
-222 fn render-line screen: (addr screen), defs: (addr handle function), bindings: (addr table), _line: (addr line), top-row: int, left-col: int, cursor-word: (addr word), cursor-col-a: (addr int) -> right-col/ecx: int {
-223   # curr-word
-224   var line/esi: (addr line) <- copy _line
-225   var first-word-ah/eax: (addr handle word) <- get line, data
-226   var curr-word/eax: (addr word) <- lookup *first-word-ah
-227   # loop-carried dependency
-228   var curr-col/ecx: int <- copy left-col
-229   #
-230   {
-231     compare curr-word, 0
-232     break-if-=
-233     # if necessary, first render columns for subsidiary stack
-234     $render-line:subsidiary: {
-235       {
-236         var display-subsidiary-stack?/eax: (addr boolean) <- get curr-word, display-subsidiary-stack?
-237         compare *display-subsidiary-stack?, 0  # false
-238         break-if-= $render-line:subsidiary
-239       }
-240       # does function exist?
-241       var callee/edi: (addr function) <- copy 0
-242       {
-243         var curr-stream-storage: (stream byte 0x10)
-244         var curr-stream/esi: (addr stream byte) <- address curr-stream-storage
-245         emit-word curr-word, curr-stream
-246         var callee-h: (handle function)
-247         var callee-ah/eax: (addr handle function) <- address callee-h
-248         find-function defs, curr-stream, callee-ah
-249         var _callee/eax: (addr function) <- lookup *callee-ah
-250         callee <- copy _callee
-251         compare callee, 0
-252         break-if-= $render-line:subsidiary
-253       }
-254       move-cursor screen, top-row, curr-col
-255       print-word screen, curr-word
-256       {
-257         var word-len/eax: int <- word-length curr-word
-258         curr-col <- add word-len
-259         curr-col <- add 2
-260         add-to top-row, 1
-261       }
-262       # obtain stack at call site
-263       var stack-storage: value-stack
-264       var stack/edx: (addr value-stack) <- address stack-storage
-265       initialize-value-stack stack, 0x10
-266       {
-267         var prev-word-ah/eax: (addr handle word) <- get curr-word, prev
-268         var prev-word/eax: (addr word) <- lookup *prev-word-ah
-269         compare prev-word, 0
-270         break-if-=
-271         evaluate defs, bindings, line, prev-word, stack
-272       }
-273       # construct new bindings
-274       var callee-bindings-storage: table
-275       var callee-bindings/esi: (addr table) <- address callee-bindings-storage
-276       initialize-table callee-bindings, 0x10
-277       bind-args callee, stack, callee-bindings
-278       # obtain body
-279       var callee-body-ah/eax: (addr handle line) <- get callee, body
-280       var callee-body/eax: (addr line) <- lookup *callee-body-ah
-281       # - render subsidiary stack
-282       curr-col <- render-line screen, defs, callee-bindings, callee-body, top-row, curr-col, cursor-word, cursor-col-a
-283       #
-284       move-cursor screen, top-row, curr-col
-285       print-code-point screen, 0x21d7  # ⇗
-286       #
-287       curr-col <- add 2
-288       subtract-from top-row, 1
-289     }
-290     # now render main column
-291     curr-col <- render-column screen, defs, bindings, line, curr-word, top-row, curr-col, cursor-word, cursor-col-a
-292     var next-word-ah/edx: (addr handle word) <- get curr-word, next
-293     curr-word <- lookup *next-word-ah
-294     loop
-295   }
-296   right-col <- copy curr-col
-297 }
-298 
-299 # Render:
-300 #   - starting at top-row, left-col: final-word
-301 #   - starting somewhere below at left-col: the stack result from interpreting first-world to final-word (inclusive)
-302 #     unless final-word is truly the final word, in which case it might be incomplete
-303 #
-304 # Outputs:
-305 # - Return the farthest column written.
-306 # - If final-word is same as cursor-word, do some additional computation to set
-307 #   cursor-col-a.
-308 fn render-column screen: (addr screen), defs: (addr handle function), bindings: (addr table), scratch: (addr line), final-word: (addr word), top-row: int, left-col: int, cursor-word: (addr word), cursor-col-a: (addr int) -> right-col/ecx: int {
-309   var max-width/ecx: int <- copy 0
-310   {
-311     # indent stack
-312     var indented-col/ebx: int <- copy left-col
-313     indented-col <- add 1  # margin-right - 2 for padding spaces
-314     # compute stack
-315     var stack: value-stack
-316     var stack-addr/edi: (addr value-stack) <- address stack
-317     initialize-value-stack stack-addr, 0x10  # max-words
-318     evaluate defs, bindings, scratch, final-word, stack-addr
-319     # render stack
-320     var curr-row/edx: int <- copy top-row
-321     curr-row <- add 3  # stack-margin-top
-322     var _max-width/eax: int <- value-stack-max-width stack-addr
-323     var max-width/esi: int <- copy _max-width
-324     var i/eax: int <- value-stack-length stack-addr
-325     {
-326       compare i, 0
-327       break-if-<=
-328       move-cursor screen, curr-row, indented-col
-329       {
-330         var val/eax: int <- pop-int-from-value-stack stack-addr
-331         render-integer screen, val, max-width
-332         var size/eax: int <- decimal-size val
-333         compare size, max-width
-334         break-if-<=
-335         max-width <- copy size
-336       }
-337       curr-row <- increment
-338       i <- decrement
-339       loop
-340     }
-341   }
-342 
-343   # render word, initialize result
-344   reset-formatting screen
-345   move-cursor screen, top-row, left-col
-346   print-word screen, final-word
-347   {
-348     var size/eax: int <- word-length final-word
-349     compare size, max-width
-350     break-if-<=
-351     max-width <- copy size
-352   }
-353 
-354   # update cursor
-355   {
-356     var f/eax: (addr word) <- copy final-word
-357     compare f, cursor-word
-358     break-if-!=
-359     var cursor-index/eax: int <- cursor-index cursor-word
-360     cursor-index <- add left-col
-361     var dest/edi: (addr int) <- copy cursor-col-a
-362     copy-to *dest, cursor-index
-363   }
-364 
-365   # post-process right-col
-366   right-col <- copy max-width
-367   right-col <- add left-col
-368   right-col <- add 3  # margin-right
-369 }
-370 
-371 # synaesthesia
-372 fn render-integer screen: (addr screen), val: int, max-width: int {
-373   var bg/eax: int <- hash-color val
-374   var fg/ecx: int <- copy 7
-375   {
-376     compare bg, 2
-377     break-if-!=
-378     fg <- copy 0
-379   }
-380   {
-381     compare bg, 3
-382     break-if-!=
-383     fg <- copy 0
-384   }
-385   {
-386     compare bg, 6
-387     break-if-!=
-388     fg <- copy 0
-389   }
-390   start-color screen, fg, bg
-391   print-grapheme screen, 0x20  # space
-392   print-int32-decimal-right-justified screen, val, max-width
-393   print-grapheme screen, 0x20  # space
-394 }
-395 
-396 fn hash-color val: int -> result/eax: int {
-397   result <- try-modulo val, 7  # assumes that 7 is always the background color
-398 }
-399 
-400 fn clear-canvas _env: (addr environment) {
-401   var env/esi: (addr environment) <- copy _env
-402   var screen-ah/edi: (addr handle screen) <- get env, screen
-403   var _screen/eax: (addr screen) <- lookup *screen-ah
-404   var screen/edi: (addr screen) <- copy _screen
-405   clear-screen screen
-406   var nrows/eax: (addr int) <- get env, nrows
-407   var _repl-col/ecx: (addr int) <- get env, code-separator-col
-408   var repl-col/ecx: int <- copy *_repl-col
-409   draw-vertical-line screen, 1, *nrows, repl-col
-410   move-cursor screen, 3, 2
-411   print-string screen, "x 2* = x 2 *"
-412   move-cursor screen, 4, 2
-413   print-string screen, "x 1+ = x 1 +"
-414   move-cursor screen, 5, 2
-415   print-string screen, "x 2+ = x 1+ 1+"
-416 }
-417 
-418 fn real-grapheme? g: grapheme -> result/eax: boolean {
-419 $real-grapheme?:body: {
-420   # if g == newline return true
-421   compare g, 0xa
-422   {
-423     break-if-!=
-424     result <- copy 1  # true
-425     break $real-grapheme?:body
-426   }
-427   # if g == tab return true
-428   compare g, 9
-429   {
-430     break-if-!=
-431     result <- copy 1  # true
-432     break $real-grapheme?:body
-433   }
-434   # if g < 32 return false
-435   compare g, 0x20
-436   {
-437     break-if->=
-438     result <- copy 0  # false
-439     break $real-grapheme?:body
-440   }
-441   # if g <= 255 return true
-442   compare g, 0xff
-443   {
-444     break-if->
-445     result <- copy 1  # true
-446     break $real-grapheme?:body
-447   }
-448   # if (g&0xff == Esc) it's an escape sequence
-449   and-with g, 0xff
-450   compare g, 0x1b  # Esc
-451   {
-452     break-if-!=
-453     result <- copy 0  # false
-454     break $real-grapheme?:body
-455   }
-456   # otherwise return true
-457   result <- copy 1  # true
-458 }
-459 }
+   1 type environment {
+   2   screen: (handle screen)
+   3   functions: (handle function)
+   4   sandboxes: (handle sandbox)
+   5   nrows: int
+   6   ncols: int
+   7   code-separator-col: int
+   8 }
+   9 
+  10 fn initialize-environment _env: (addr environment) {
+  11   var env/esi: (addr environment) <- copy _env
+  12   # initialize some predefined function definitions
+  13   var functions/eax: (addr handle function) <- get env, functions
+  14   create-primitive-functions functions
+  15   # initialize first sandbox
+  16   var sandbox-ah/eax: (addr handle sandbox) <- get env, sandboxes
+  17   allocate sandbox-ah
+  18   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
+  19   initialize-sandbox sandbox
+  20   # initialize screen
+  21   var screen-ah/eax: (addr handle screen) <- get env, screen
+  22   var _screen/eax: (addr screen) <- lookup *screen-ah
+  23   var screen/edi: (addr screen) <- copy _screen
+  24   var nrows/eax: int <- copy 0
+  25   var ncols/ecx: int <- copy 0
+  26   nrows, ncols <- screen-size screen
+  27   var dest/edx: (addr int) <- get env, nrows
+  28   copy-to *dest, nrows
+  29   dest <- get env, ncols
+  30   copy-to *dest, ncols
+  31   var repl-col/ecx: int <- copy ncols
+  32   repl-col <- shift-right 1
+  33   dest <- get env, code-separator-col
+  34   copy-to *dest, repl-col
+  35 }
+  36 
+  37 fn draw-screen _env: (addr environment) {
+  38   var env/esi: (addr environment) <- copy _env
+  39   var screen-ah/eax: (addr handle screen) <- get env, screen
+  40   var _screen/eax: (addr screen) <- lookup *screen-ah
+  41   var screen/edi: (addr screen) <- copy _screen
+  42   var dest/edx: (addr int) <- get env, code-separator-col
+  43   var tmp/eax: int <- copy *dest
+  44   clear-canvas env
+  45   tmp <- add 2  # repl-margin-left
+  46   move-cursor screen, 3, tmp  # input-row
+  47 }
+  48 
+  49 fn initialize-environment-with-fake-screen _self: (addr environment), nrows: int, ncols: int {
+  50   var self/esi: (addr environment) <- copy _self
+  51   var screen-ah/eax: (addr handle screen) <- get self, screen
+  52   allocate screen-ah
+  53   var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  54   initialize-screen screen-addr, nrows, ncols
+  55   initialize-environment self
+  56 }
+  57 
+  58 #############
+  59 # Iterate
+  60 #############
+  61 
+  62 fn process _self: (addr environment), key: grapheme {
+  63 $process:body: {
+  64   var self/esi: (addr environment) <- copy _self
+  65   var sandbox-ah/eax: (addr handle sandbox) <- get self, sandboxes
+  66   var _sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
+  67   var sandbox/edi: (addr sandbox) <- copy _sandbox
+  68   var rename-word-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-cursor-word
+  69   var rename-word-mode?/eax: (addr word) <- lookup *rename-word-mode-ah?
+  70   compare rename-word-mode?, 0
+  71   {
+  72     break-if-=
+  73 #?     print-string 0, "processing sandbox rename\n"
+  74     process-sandbox-rename sandbox, key
+  75     break $process:body
+  76   }
+  77   var define-function-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-function
+  78   var define-function-mode?/eax: (addr word) <- lookup *define-function-mode-ah?
+  79   compare define-function-mode?, 0
+  80   {
+  81     break-if-=
+  82 #?     print-string 0, "processing function definition\n"
+  83     var functions/ecx: (addr handle function) <- get self, functions
+  84     process-sandbox-define sandbox, functions, key
+  85     break $process:body
+  86   }
+  87 #?   print-string 0, "processing sandbox\n"
+  88   process-sandbox self, sandbox, key
+  89 }
+  90 }
+  91 
+  92 fn process-sandbox _self: (addr environment), _sandbox: (addr sandbox), key: grapheme {
+  93 $process-sandbox:body: {
+  94   var self/esi: (addr environment) <- copy _self
+  95   var sandbox/edi: (addr sandbox) <- copy _sandbox
+  96   var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+  97   var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+  98   var cursor-word-ah/ebx: (addr handle word) <- get cursor-call-path, word
+  99   var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 100   var cursor-word/ecx: (addr word) <- copy _cursor-word
+ 101   compare key, 0x445b1b  # left-arrow
+ 102   $process-sandbox:key-left-arrow: {
+ 103     break-if-!=
+ 104 #?     print-string 0, "left-arrow\n"
+ 105     # if not at start, move left within current word
+ 106     var at-start?/eax: boolean <- cursor-at-start? cursor-word
+ 107     compare at-start?, 0  # false
+ 108     {
+ 109       break-if-!=
+ 110 #?       print-string 0, "cursor left within word\n"
+ 111       cursor-left cursor-word
+ 112       break $process-sandbox:body
+ 113     }
+ 114     # if current word is expanded, move to the rightmost word in its body
+ 115     {
+ 116       var cursor-call-path/esi: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 117       var expanded-words/edx: (addr handle call-path) <- get sandbox, expanded-words
+ 118       var curr-word-is-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path
+ 119       compare curr-word-is-expanded?, 0  # false
+ 120       break-if-=
+ 121       # update cursor-call-path
+ 122 #?       print-string 0, "curr word is expanded\n"
+ 123       var self/ecx: (addr environment) <- copy _self
+ 124       var functions/ecx: (addr handle function) <- get self, functions
+ 125       var body: (handle line)
+ 126       var body-ah/eax: (addr handle line) <- address body
+ 127       function-body functions, cursor-word-ah, body-ah
+ 128       var body-addr/eax: (addr line) <- lookup *body-ah
+ 129       var first-word-ah/edx: (addr handle word) <- get body-addr, data
+ 130       var final-word-h: (handle word)
+ 131       var final-word-ah/eax: (addr handle word) <- address final-word-h
+ 132       final-word first-word-ah, final-word-ah
+ 133       push-to-call-path-element cursor-call-path, final-word-ah
+ 134       # move cursor to end of word
+ 135       var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 136       var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 137       var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
+ 138       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 139       cursor-to-end cursor-word
+ 140       break $process-sandbox:body
+ 141     }
+ 142     # if at first word, look for a caller to jump to
+ 143     $process-sandbox:key-left-arrow-first-word: {
+ 144       var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
+ 145       var prev-word/eax: (addr word) <- lookup *prev-word-ah
+ 146       compare prev-word, 0
+ 147       break-if-!=
+ 148       $process-sandbox:key-left-arrow-first-word-and-caller: {
+ 149 #?         print-string 0, "return\n"
+ 150         {
+ 151           var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 152           var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 153           var next-cursor-element-ah/edx: (addr handle call-path-element) <- get cursor-call-path, next
+ 154           var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah
+ 155           compare next-cursor-element, 0
+ 156           break-if-= $process-sandbox:key-left-arrow-first-word-and-caller
+ 157           copy-object next-cursor-element-ah, cursor-call-path-ah
+ 158         }
+ 159         var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 160         var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 161         var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
+ 162         var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 163         cursor-word <- copy _cursor-word
+ 164       }
+ 165     }
+ 166     # then move to end of previous word
+ 167     var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
+ 168     var prev-word/eax: (addr word) <- lookup *prev-word-ah
+ 169     {
+ 170       compare prev-word, 0
+ 171       break-if-=
+ 172 #?       print-string 0, "previous word\n"
+ 173       cursor-to-end prev-word
+ 174       var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 175       decrement-final-element cursor-call-path
+ 176     }
+ 177     break $process-sandbox:body
+ 178   }
+ 179   compare key, 0x435b1b  # right-arrow
+ 180   $process-sandbox:key-right-arrow: {
+ 181     break-if-!=
+ 182     # if not at end, move right within current word
+ 183     var at-end?/eax: boolean <- cursor-at-end? cursor-word
+ 184     compare at-end?, 0  # false
+ 185     {
+ 186       break-if-!=
+ 187 #?       print-string 0, "a\n"
+ 188       cursor-right cursor-word
+ 189       break $process-sandbox:body
+ 190     }
+ 191     # if at final word, look for a caller to jump to
+ 192     {
+ 193       var next-word-ah/edx: (addr handle word) <- get cursor-word, next
+ 194       var next-word/eax: (addr word) <- lookup *next-word-ah
+ 195       compare next-word, 0
+ 196       break-if-!=
+ 197       var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 198       var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 199       var next-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next
+ 200       var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah
+ 201       compare next-cursor-element, 0
+ 202       break-if-=
+ 203       copy-object next-cursor-element-ah, cursor-call-path-ah
+ 204       break $process-sandbox:body
+ 205     }
+ 206     # otherwise, move to the next word
+ 207     var next-word-ah/edx: (addr handle word) <- get cursor-word, next
+ 208     var next-word/eax: (addr word) <- lookup *next-word-ah
+ 209     {
+ 210       compare next-word, 0
+ 211       break-if-=
+ 212 #?       print-string 0, "b\n"
+ 213       cursor-to-start next-word
+ 214       # . . cursor-word now out of date
+ 215       var cursor-call-path/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 216       increment-final-element cursor-call-path
+ 217       # Is the new cursor word expanded? If so, it's a function call. Add a
+ 218       # new level to the cursor-call-path for the call's body.
+ 219       $process-sandbox:key-right-arrow-next-word-is-call-expanded: {
+ 220 #?         print-string 0, "c\n"
+ 221         {
+ 222           var expanded-words/eax: (addr handle call-path) <- get sandbox, expanded-words
+ 223           var curr-word-is-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path
+ 224           compare curr-word-is-expanded?, 0  # false
+ 225           break-if-= $process-sandbox:key-right-arrow-next-word-is-call-expanded
+ 226         }
+ 227         var callee-h: (handle function)
+ 228         var callee-ah/edx: (addr handle function) <- address callee-h
+ 229         var functions/ebx: (addr handle function) <- get self, functions
+ 230         callee functions, next-word, callee-ah
+ 231         var callee/eax: (addr function) <- lookup *callee-ah
+ 232         var callee-body-ah/eax: (addr handle line) <- get callee, body
+ 233         var callee-body/eax: (addr line) <- lookup *callee-body-ah
+ 234         var callee-body-first-word/edx: (addr handle word) <- get callee-body, data
+ 235         push-to-call-path-element cursor-call-path, callee-body-first-word
+ 236         # position cursor at left
+ 237         var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 238         var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 239         var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
+ 240         var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 241         cursor-to-start cursor-word
+ 242 #?         print-string 0, "d\n"
+ 243         break $process-sandbox:body
+ 244       }
+ 245     }
+ 246     break $process-sandbox:body
+ 247   }
+ 248   compare key, 0xa  # enter
+ 249   {
+ 250     break-if-!=
+ 251     # toggle display of subsidiary stack
+ 252     toggle-cursor-word sandbox
+ 253     break $process-sandbox:body
+ 254   }
+ 255   # word-based motions
+ 256   compare key, 2  # ctrl-b
+ 257   $process-sandbox:prev-word: {
+ 258     break-if-!=
+ 259     # jump to previous word at same level
+ 260     var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev
+ 261     var prev-word/eax: (addr word) <- lookup *prev-word-ah
+ 262     {
+ 263       compare prev-word, 0
+ 264       break-if-=
+ 265       cursor-to-end prev-word
+ 266       var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 267       decrement-final-element cursor-call-path
+ 268       break $process-sandbox:body
+ 269     }
+ 270     # if previous word doesn't exist, try to bump up one level
+ 271     {
+ 272       var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 273       var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 274       var caller-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next
+ 275       var caller-cursor-element/eax: (addr call-path-element) <- lookup *caller-cursor-element-ah
+ 276       compare caller-cursor-element, 0
+ 277       break-if-=
+ 278       # check if previous word exists in caller
+ 279       var caller-word-ah/eax: (addr handle word) <- get caller-cursor-element, word
+ 280       var caller-word/eax: (addr word) <- lookup *caller-word-ah
+ 281       var word-before-caller-ah/eax: (addr handle word) <- get caller-word, prev
+ 282       var word-before-caller/eax: (addr word) <- lookup *word-before-caller-ah
+ 283       compare word-before-caller, 0
+ 284       break-if-=
+ 285       # if so jump to it
+ 286       drop-from-call-path-element cursor-call-path-ah
+ 287       decrement-final-element cursor-call-path-ah
+ 288       break $process-sandbox:body
+ 289     }
+ 290   }
+ 291   compare key, 6  # ctrl-f
+ 292   $process-sandbox:next-word: {
+ 293     break-if-!=
+ 294 #?     print-string 0, "AA\n"
+ 295     # jump to previous word at same level
+ 296     var next-word-ah/edx: (addr handle word) <- get cursor-word, next
+ 297     var next-word/eax: (addr word) <- lookup *next-word-ah
+ 298     {
+ 299       compare next-word, 0
+ 300       break-if-=
+ 301 #?       print-string 0, "BB\n"
+ 302       cursor-to-end next-word
+ 303       var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 304       increment-final-element cursor-call-path
+ 305       break $process-sandbox:body
+ 306     }
+ 307     # if next word doesn't exist, try to bump up one level
+ 308 #?     print-string 0, "CC\n"
+ 309     var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 310     var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 311     var caller-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next
+ 312     var caller-cursor-element/eax: (addr call-path-element) <- lookup *caller-cursor-element-ah
+ 313     compare caller-cursor-element, 0
+ 314     break-if-=
+ 315 #?     print-string 0, "DD\n"
+ 316     copy-object caller-cursor-element-ah, cursor-call-path-ah
+ 317     break $process-sandbox:body
+ 318   }
+ 319   # line-based motions
+ 320   compare key, 1  # ctrl-a
+ 321   $process-sandbox:start-of-line: {
+ 322     break-if-!=
+ 323     # move cursor up past all calls and to start of line
+ 324     var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 325     drop-nested-calls cursor-call-path-ah
+ 326     move-final-element-to-start-of-line cursor-call-path-ah
+ 327     # move cursor to start of initial word
+ 328     var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 329     var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
+ 330     var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 331     cursor-to-start cursor-word
+ 332     # this works as long as the first word isn't expanded
+ 333     # but we don't expect to see zero-arg functions first-up
+ 334     break $process-sandbox:body
+ 335   }
+ 336   compare key, 5  # ctrl-e
+ 337   $process-sandbox:end-of-line: {
+ 338     break-if-!=
+ 339     # move cursor to final word of sandbox
+ 340     var cursor-call-path-ah/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 341     initialize-path-from-sandbox sandbox, cursor-call-path-ah
+ 342     var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 343     var dest/eax: (addr handle word) <- get cursor-call-path, word
+ 344     final-word dest, dest
+ 345     # move cursor to end of final word
+ 346     var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 347     cursor-to-end cursor-word
+ 348     # this works because expanded words lie to the right of their bodies
+ 349     # so the final word is always guaranteed to be at the top-level
+ 350     break $process-sandbox:body
+ 351   }
+ 352   compare key, 0x15  # ctrl-u
+ 353   $process-sandbox:clear-line: {
+ 354     break-if-!=
+ 355     # clear line in sandbox
+ 356     initialize-sandbox sandbox
+ 357     break $process-sandbox:body
+ 358   }
+ 359   # if cursor is within a call, disable editing hotkeys below
+ 360   var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 361   var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 362   var next-cursor-element-ah/eax: (addr handle call-path-element) <- get cursor-call-path, next
+ 363   var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah
+ 364   compare next-cursor-element, 0
+ 365   break-if-!= $process-sandbox:body
+ 366   # - remaining keys only work at the top row outside any function calls
+ 367   compare key, 0x7f  # del (backspace on Macs)
+ 368   $process-sandbox:backspace: {
+ 369     break-if-!=
+ 370     # if not at start of some word, delete grapheme before cursor within current word
+ 371     var at-start?/eax: boolean <- cursor-at-start? cursor-word
+ 372     compare at-start?, 0  # false
+ 373     {
+ 374       break-if-!=
+ 375       delete-before-cursor cursor-word
+ 376       break $process-sandbox:body
+ 377     }
+ 378     # otherwise delete current word and move to end of prev word
+ 379     var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev
+ 380     var prev-word/eax: (addr word) <- lookup *prev-word-ah
+ 381     {
+ 382       compare prev-word, 0
+ 383       break-if-=
+ 384       cursor-to-end prev-word
+ 385       delete-next prev-word
+ 386       var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 387       decrement-final-element cursor-call-path
+ 388     }
+ 389     break $process-sandbox:body
+ 390   }
+ 391   compare key, 0x20  # space
+ 392   $process-sandbox:space: {
+ 393     break-if-!=
+ 394     # insert new word
+ 395     append-word cursor-word-ah
+ 396     var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 397     increment-final-element cursor-call-path
+ 398     break $process-sandbox:body
+ 399   }
+ 400   compare key, 0xe  # ctrl-n
+ 401   $process:rename-word: {
+ 402     break-if-!=
+ 403     # TODO: ensure current word is not a function
+ 404     # rename word at cursor
+ 405     var new-name-ah/eax: (addr handle word) <- get sandbox, partial-name-for-cursor-word
+ 406     allocate new-name-ah
+ 407     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 408     initialize-word new-name
+ 409     break $process-sandbox:body
+ 410   }
+ 411   compare key, 4  # ctrl-d
+ 412   $process:define-function: {
+ 413     break-if-!=
+ 414     # define function out of line at cursor
+ 415     var new-name-ah/eax: (addr handle word) <- get sandbox, partial-name-for-function
+ 416     allocate new-name-ah
+ 417     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 418     initialize-word new-name
+ 419     break $process-sandbox:body
+ 420   }
+ 421   # otherwise insert key within current word
+ 422   var g/edx: grapheme <- copy key
+ 423   var print?/eax: boolean <- real-grapheme? key
+ 424   $process-sandbox:real-grapheme: {
+ 425     compare print?, 0  # false
+ 426     break-if-=
+ 427     add-grapheme-to-word cursor-word, g
+ 428     break $process-sandbox:body
+ 429   }
+ 430   # silently ignore other hotkeys
+ 431 }
+ 432 }
+ 433 
+ 434 # collect new name in partial-name-for-cursor-word, and then rename the word
+ 435 # at cursor to it
+ 436 # Precondition: cursor-call-path is a singleton (not within a call)
+ 437 fn process-sandbox-rename _sandbox: (addr sandbox), key: grapheme {
+ 438 $process-sandbox-rename:body: {
+ 439   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 440   var new-name-ah/edi: (addr handle word) <- get sandbox, partial-name-for-cursor-word
+ 441   # if 'esc' pressed, cancel rename
+ 442   compare key, 0x1b  # esc
+ 443   $process-sandbox-rename:cancel: {
+ 444     break-if-!=
+ 445     var empty: (handle word)
+ 446     copy-handle empty, new-name-ah
+ 447     break $process-sandbox-rename:body
+ 448   }
+ 449   # if 'enter' pressed, perform rename
+ 450   compare key, 0xa  # enter
+ 451   $process-sandbox-rename:commit: {
+ 452     break-if-!=
+ 453 #?     print-string 0, "rename\n"
+ 454     # new line
+ 455     var new-line-h: (handle line)
+ 456     var new-line-ah/eax: (addr handle line) <- address new-line-h
+ 457     allocate new-line-ah
+ 458     var new-line/eax: (addr line) <- lookup *new-line-ah
+ 459     initialize-line new-line
+ 460     var new-line-word-ah/ecx: (addr handle word) <- get new-line, data
+ 461     {
+ 462       # move word at cursor to new line
+ 463       var cursor-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 464       var cursor/eax: (addr call-path-element) <- lookup *cursor-ah
+ 465       var word-at-cursor-ah/eax: (addr handle word) <- get cursor, word
+ 466 #?       print-string 0, "cursor before at word "
+ 467 #?       {
+ 468 #?         var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah
+ 469 #?         print-word 0, cursor-word
+ 470 #?         print-string 0, "\n"
+ 471 #?       }
+ 472       move-word-contents word-at-cursor-ah, new-line-word-ah
+ 473       # copy name to word at cursor
+ 474       copy-word-contents-before-cursor new-name-ah, word-at-cursor-ah
+ 475 #?       print-string 0, "cursor after at word "
+ 476 #?       {
+ 477 #?         var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah
+ 478 #?         print-word 0, cursor-word
+ 479 #?         print-string 0, "\n"
+ 480 #?         var foo/eax: int <- copy cursor-word
+ 481 #?         print-int32-hex 0, foo
+ 482 #?         print-string 0, "\n"
+ 483 #?       }
+ 484 #?       print-string 0, "new name word "
+ 485 #?       {
+ 486 #?         var new-name/eax: (addr word) <- lookup *new-name-ah
+ 487 #?         print-word 0, new-name
+ 488 #?         print-string 0, "\n"
+ 489 #?         var foo/eax: int <- copy new-name
+ 490 #?         print-int32-hex 0, foo
+ 491 #?         print-string 0, "\n"
+ 492 #?       }
+ 493     }
+ 494     # prepend '=' to name
+ 495     {
+ 496       var new-name/eax: (addr word) <- lookup *new-name-ah
+ 497       cursor-to-start new-name
+ 498       add-grapheme-to-word new-name, 0x3d  # '='
+ 499     }
+ 500     # append name to new line
+ 501     chain-words new-line-word-ah, new-name-ah
+ 502     # new-line->next = sandbox->data
+ 503     var new-line-next/ecx: (addr handle line) <- get new-line, next
+ 504     var sandbox-slot/edx: (addr handle line) <- get sandbox, data
+ 505     copy-object sandbox-slot, new-line-next
+ 506     # sandbox->data = new-line
+ 507     copy-handle new-line-h, sandbox-slot
+ 508     # clear partial-name-for-cursor-word
+ 509     var empty: (handle word)
+ 510     copy-handle empty, new-name-ah
+ 511 #?     # XXX
+ 512 #?     var cursor-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 513 #?     var cursor/eax: (addr call-path-element) <- lookup *cursor-ah
+ 514 #?     var word-at-cursor-ah/eax: (addr handle word) <- get cursor, word
+ 515 #?     print-string 0, "cursor after rename: "
+ 516 #?     {
+ 517 #?       var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah
+ 518 #?       print-word 0, cursor-word
+ 519 #?       print-string 0, " -- "
+ 520 #?       var foo/eax: int <- copy cursor-word
+ 521 #?       print-int32-hex 0, foo
+ 522 #?       print-string 0, "\n"
+ 523 #?     }
+ 524     break $process-sandbox-rename:body
+ 525   }
+ 526   #
+ 527   compare key, 0x7f  # del (backspace on Macs)
+ 528   $process-sandbox-rename:backspace: {
+ 529     break-if-!=
+ 530     # if not at start, delete grapheme before cursor
+ 531     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 532     var at-start?/eax: boolean <- cursor-at-start? new-name
+ 533     compare at-start?, 0  # false
+ 534     {
+ 535       break-if-!=
+ 536       var new-name/eax: (addr word) <- lookup *new-name-ah
+ 537       delete-before-cursor new-name
+ 538     }
+ 539     break $process-sandbox-rename:body
+ 540   }
+ 541   # otherwise insert key within current word
+ 542   var print?/eax: boolean <- real-grapheme? key
+ 543   $process-sandbox-rename:real-grapheme: {
+ 544     compare print?, 0  # false
+ 545     break-if-=
+ 546     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 547     add-grapheme-to-word new-name, key
+ 548     break $process-sandbox-rename:body
+ 549   }
+ 550   # silently ignore other hotkeys
+ 551 }
+ 552 }
+ 553 
+ 554 # collect new name in partial-name-for-function, and then define the last line
+ 555 # of the sandbox to be a new function with that name. Replace the last line
+ 556 # with a call to the appropriate function.
+ 557 # Precondition: cursor-call-path is a singleton (not within a call)
+ 558 fn process-sandbox-define _sandbox: (addr sandbox), functions: (addr handle function), key: grapheme {
+ 559 $process-sandbox-define:body: {
+ 560   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 561   var new-name-ah/edi: (addr handle word) <- get sandbox, partial-name-for-function
+ 562   # if 'esc' pressed, cancel define
+ 563   compare key, 0x1b  # esc
+ 564   $process-sandbox-define:cancel: {
+ 565     break-if-!=
+ 566     var empty: (handle word)
+ 567     copy-handle empty, new-name-ah
+ 568     break $process-sandbox-define:body
+ 569   }
+ 570   # if 'enter' pressed, perform define
+ 571   compare key, 0xa  # enter
+ 572   $process-sandbox-define:commit: {
+ 573     break-if-!=
+ 574 #?     print-string 0, "define\n"
+ 575     # create new function
+ 576     var new-function: (handle function)
+ 577     var new-function-ah/ecx: (addr handle function) <- address new-function
+ 578     allocate new-function-ah
+ 579     var _new-function/eax: (addr function) <- lookup *new-function-ah
+ 580     var new-function/ebx: (addr function) <- copy _new-function
+ 581     var dest/edx: (addr handle function) <- get new-function, next
+ 582     copy-object functions, dest
+ 583     copy-object new-function-ah, functions
+ 584     # set function name to new-name
+ 585     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 586     var dest/edx: (addr handle array byte) <- get new-function, name
+ 587     word-to-string new-name, dest
+ 588     # move final line to body
+ 589     var body-ah/eax: (addr handle line) <- get new-function, body
+ 590     allocate body-ah
+ 591     var body/eax: (addr line) <- lookup *body-ah
+ 592     var body-contents/ecx: (addr handle word) <- get body, data
+ 593     var final-line-storage: (handle line)
+ 594     var final-line-ah/eax: (addr handle line) <- address final-line-storage
+ 595     final-line sandbox, final-line-ah
+ 596     var final-line/eax: (addr line) <- lookup *final-line-ah
+ 597     var final-line-contents/eax: (addr handle word) <- get final-line, data
+ 598     copy-object final-line-contents, body-contents
+ 599     #
+ 600     copy-unbound-words-to-args functions
+ 601     #
+ 602     var empty-word: (handle word)
+ 603     copy-handle empty-word, final-line-contents
+ 604     construct-call functions, final-line-contents
+ 605     # clear partial-name-for-function
+ 606     var empty-word: (handle word)
+ 607     copy-handle empty-word, new-name-ah
+ 608     # update cursor
+ 609     var final-line/eax: (addr line) <- lookup final-line-storage
+ 610     var cursor-call-path-ah/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 611     allocate cursor-call-path-ah  # leak
+ 612     initialize-path-from-line final-line, cursor-call-path-ah
+ 613     break $process-sandbox-define:body
+ 614   }
+ 615   #
+ 616   compare key, 0x7f  # del (backspace on Macs)
+ 617   $process-sandbox-define:backspace: {
+ 618     break-if-!=
+ 619     # if not at start, delete grapheme before cursor
+ 620     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 621     var at-start?/eax: boolean <- cursor-at-start? new-name
+ 622     compare at-start?, 0  # false
+ 623     {
+ 624       break-if-!=
+ 625       var new-name/eax: (addr word) <- lookup *new-name-ah
+ 626       delete-before-cursor new-name
+ 627     }
+ 628     break $process-sandbox-define:body
+ 629   }
+ 630   # otherwise insert key within current word
+ 631   var print?/eax: boolean <- real-grapheme? key
+ 632   $process-sandbox-define:real-grapheme: {
+ 633     compare print?, 0  # false
+ 634     break-if-=
+ 635     var new-name/eax: (addr word) <- lookup *new-name-ah
+ 636     add-grapheme-to-word new-name, key
+ 637     break $process-sandbox-define:body
+ 638   }
+ 639   # silently ignore other hotkeys
+ 640 }
+ 641 }
+ 642 
+ 643 # extract from the body of the first function in 'functions' all words that
+ 644 # aren't defined in the rest of 'functions'. Prepend them in reverse order.
+ 645 # Assumes function body is a single line for now.
+ 646 fn copy-unbound-words-to-args _functions: (addr handle function) {
+ 647   # target
+ 648   var target-ah/eax: (addr handle function) <- copy _functions
+ 649   var _target/eax: (addr function) <- lookup *target-ah
+ 650   var target/esi: (addr function) <- copy _target
+ 651   var dest-ah/edi: (addr handle word) <- get target, args
+ 652   # next
+ 653   var functions-ah/edx: (addr handle function) <- get target, next
+ 654   # src
+ 655   var line-ah/eax: (addr handle line) <- get target, body
+ 656   var line/eax: (addr line) <- lookup *line-ah
+ 657   var curr-ah/eax: (addr handle word) <- get line, data
+ 658   var curr/eax: (addr word) <- lookup *curr-ah
+ 659   {
+ 660     compare curr, 0
+ 661     break-if-=
+ 662     $copy-unbound-words-to-args:loop-iter: {
+ 663       # is it a number?
+ 664       {
+ 665         var is-int?/eax: boolean <- word-is-decimal-integer? curr
+ 666         compare is-int?, 0  # false
+ 667         break-if-!= $copy-unbound-words-to-args:loop-iter
+ 668       }
+ 669       # is it a pre-existing function?
+ 670       var bound?/ebx: boolean <- bound-function? curr, functions-ah
+ 671       compare bound?, 0  # false
+ 672       break-if-!=
+ 673       # is it already bound as an arg?
+ 674       var dup?/ebx: boolean <- arg-exists? _functions, curr  # _functions = target-ah
+ 675       compare dup?, 0  # false
+ 676       break-if-!= $copy-unbound-words-to-args:loop-iter
+ 677       # push copy of curr before dest-ah
+ 678       var rest-h: (handle word)
+ 679       var rest-ah/ecx: (addr handle word) <- address rest-h
+ 680       copy-object dest-ah, rest-ah
+ 681       copy-word curr, dest-ah
+ 682       chain-words dest-ah, rest-ah
+ 683     }
+ 684     var next-ah/ecx: (addr handle word) <- get curr, next
+ 685     curr <- lookup *next-ah
+ 686     loop
+ 687   }
+ 688 }
+ 689 
+ 690 fn bound-function? w: (addr word), functions-ah: (addr handle function) -> result/ebx: boolean {
+ 691   result <- copy 1  # true
+ 692   # if w == "+" return true
+ 693   var subresult/eax: boolean <- word-equal? w, "+"
+ 694   compare subresult, 0  # false
+ 695   break-if-!=
+ 696   # if w == "-" return true
+ 697   subresult <- word-equal? w, "-"
+ 698   compare subresult, 0  # false
+ 699   break-if-!=
+ 700   # if w == "*" return true
+ 701   subresult <- word-equal? w, "*"
+ 702   compare subresult, 0  # false
+ 703   break-if-!=
+ 704   # return w in functions
+ 705   var out-h: (handle function)
+ 706   var out/eax: (addr handle function) <- address out-h
+ 707   callee functions-ah, w, out
+ 708   var found?/eax: (addr function) <- lookup *out
+ 709   result <- copy found?
+ 710 }
+ 711 
+ 712 fn arg-exists? _f-ah: (addr handle function), arg: (addr word) -> result/ebx: boolean {
+ 713   var f-ah/eax: (addr handle function) <- copy *_f-ah
+ 714   var f/eax: (addr function) <- lookup *f-ah
+ 715   var args-ah/eax: (addr handle word) <- get f, args
+ 716   result <- word-exists? args-ah, arg
+ 717 }
+ 718 
+ 719 # construct a call to `f` with copies of exactly its args
+ 720 fn construct-call _f-ah: (addr handle function), _dest-ah: (addr handle word) {
+ 721   var f-ah/eax: (addr handle function) <- copy _f-ah
+ 722   var _f/eax: (addr function) <- lookup *f-ah
+ 723   var f/esi: (addr function) <- copy _f
+ 724   # append args in reverse
+ 725   var args-ah/eax: (addr handle word) <- get f, args
+ 726   var dest-ah/edi: (addr handle word) <- copy _dest-ah
+ 727   copy-words-in-reverse args-ah, dest-ah
+ 728   # append name
+ 729   var name-ah/eax: (addr handle array byte) <- get f, name
+ 730   var name/eax: (addr array byte) <- lookup *name-ah
+ 731   append-word-at-end-with dest-ah, name
+ 732 }
+ 733 
+ 734 fn word-index _words: (addr handle word), _n: int, out: (addr handle word) {
+ 735 $word-index:body: {
+ 736   var n/ecx: int <- copy _n
+ 737   {
+ 738     compare n, 0
+ 739     break-if-!=
+ 740     copy-object _words, out
+ 741     break $word-index:body
+ 742   }
+ 743   var words-ah/eax: (addr handle word) <- copy _words
+ 744   var words/eax: (addr word) <- lookup *words-ah
+ 745   var next/eax: (addr handle word) <- get words, next
+ 746   n <- decrement
+ 747   word-index next, n, out
+ 748 }
+ 749 }
+ 750 
+ 751 fn toggle-cursor-word _sandbox: (addr sandbox) {
+ 752 $toggle-cursor-word:body: {
+ 753   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 754   var expanded-words/edi: (addr handle call-path) <- get sandbox, expanded-words
+ 755   var cursor-call-path/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 756 #?   print-string 0, "cursor call path: "
+ 757 #?   dump-call-path-element 0, cursor-call-path
+ 758 #?   print-string 0, "expanded words:\n"
+ 759 #?   dump-call-paths 0, expanded-words
+ 760   var already-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path
+ 761   compare already-expanded?, 0  # false
+ 762   {
+ 763     break-if-!=
+ 764 #?     print-string 0, "expand\n"
+ 765     # if not already-expanded, insert
+ 766     insert-in-call-path expanded-words cursor-call-path
+ 767 #?     print-string 0, "expanded words now:\n"
+ 768 #?     dump-call-paths 0, expanded-words
+ 769     break $toggle-cursor-word:body
+ 770   }
+ 771   {
+ 772     break-if-=
+ 773     # otherwise delete
+ 774     delete-in-call-path expanded-words cursor-call-path
+ 775   }
+ 776 }
+ 777 }
+ 778 
+ 779 #############
+ 780 # Visualize
+ 781 #############
+ 782 
+ 783 fn evaluate-environment _env: (addr environment), stack: (addr value-stack) {
+ 784   var env/esi: (addr environment) <- copy _env
+ 785   # functions
+ 786   var functions/edx: (addr handle function) <- get env, functions
+ 787   # line
+ 788   var sandbox-ah/esi: (addr handle sandbox) <- get env, sandboxes
+ 789   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
+ 790   var line-ah/eax: (addr handle line) <- get sandbox, data
+ 791   var _line/eax: (addr line) <- lookup *line-ah
+ 792   var line/esi: (addr line) <- copy _line
+ 793   evaluate functions, 0, line, 0, stack
+ 794 }
+ 795 
+ 796 fn render _env: (addr environment) {
+ 797 #?   print-string 0, "==\n"
+ 798   var env/esi: (addr environment) <- copy _env
+ 799   clear-canvas env
+ 800   # screen
+ 801   var screen-ah/eax: (addr handle screen) <- get env, screen
+ 802   var _screen/eax: (addr screen) <- lookup *screen-ah
+ 803   var screen/edi: (addr screen) <- copy _screen
+ 804   # repl-col
+ 805   var _repl-col/eax: (addr int) <- get env, code-separator-col
+ 806   var repl-col/ecx: int <- copy *_repl-col
+ 807   repl-col <- add 2  # repl-margin-left
+ 808   # functions
+ 809   var functions/edx: (addr handle function) <- get env, functions
+ 810   # sandbox
+ 811   var sandbox-ah/eax: (addr handle sandbox) <- get env, sandboxes
+ 812   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
+ 813 #?   {
+ 814 #?     var line-ah/eax: (addr handle line) <- get sandbox, data
+ 815 #?     var line/eax: (addr line) <- lookup *line-ah
+ 816 #?     var first-word-ah/eax: (addr handle word) <- get line, data
+ 817 #?     var curr-word/eax: (addr word) <- lookup *first-word-ah
+ 818 #?     print-word 0, curr-word
+ 819 #?     print-string 0, "\n"
+ 820 #?   }
+ 821   # bindings
+ 822   var bindings-storage: table
+ 823   var bindings/ebx: (addr table) <- address bindings-storage
+ 824   initialize-table bindings, 0x10
+ 825   render-sandbox screen, functions, bindings, sandbox, 3, repl-col
+ 826 }
+ 827 
+ 828 fn render-sandbox screen: (addr screen), functions: (addr handle function), bindings: (addr table), _sandbox: (addr sandbox), top-row: int, left-col: int {
+ 829   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 830   # line
+ 831   var curr-line-ah/eax: (addr handle line) <- get sandbox, data
+ 832   var _curr-line/eax: (addr line) <- lookup *curr-line-ah
+ 833   var curr-line/ecx: (addr line) <- copy _curr-line
+ 834   #
+ 835   var curr-row/edx: int <- copy top-row
+ 836   # cursor row, col
+ 837   var cursor-row: int
+ 838   var cursor-row-addr: (addr int)
+ 839   var tmp/eax: (addr int) <- address cursor-row
+ 840   copy-to cursor-row-addr, tmp
+ 841   var cursor-col: int
+ 842   var cursor-col-addr: (addr int)
+ 843   tmp <- address cursor-col
+ 844   copy-to cursor-col-addr, tmp
+ 845   # render all but final line without stack
+ 846 #?   print-string 0, "render all but final line\n"
+ 847   {
+ 848     var next-line-ah/eax: (addr handle line) <- get curr-line, next
+ 849     var next-line/eax: (addr line) <- lookup *next-line-ah
+ 850     compare next-line, 0
+ 851     break-if-=
+ 852     {
+ 853       var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 854       var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 855       var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
+ 856       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 857 #?       print-string 0, "cursor 2: "
+ 858 #?       {
+ 859 #?         print-word 0, cursor-word
+ 860 #?         print-string 0, " -- "
+ 861 #?         var foo/eax: int <- copy cursor-word
+ 862 #?         print-int32-hex 0, foo
+ 863 #?         print-string 0, "\n"
+ 864 #?       }
+ 865       # 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
+ 866       render-line-without-stack screen, curr-line, curr-row, left-col, cursor-word, cursor-row-addr, cursor-col-addr
+ 867     }
+ 868     curr-line <- copy next-line
+ 869     curr-row <- add 2
+ 870     loop
+ 871   }
+ 872   #
+ 873 #?   print-string 0, "render final line\n"
+ 874   render-final-line-with-stack screen, functions, bindings, sandbox, curr-row, left-col, cursor-row-addr, cursor-col-addr
+ 875   # at most one of the following dialogs will be rendered
+ 876   render-rename-dialog screen, sandbox, cursor-row, cursor-col
+ 877   render-define-dialog screen, sandbox, cursor-row, cursor-col
+ 878   move-cursor screen, cursor-row, cursor-col
+ 879 }
+ 880 
+ 881 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) {
+ 882   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 883   # expanded-words
+ 884   var expanded-words/edi: (addr handle call-path) <- get sandbox, expanded-words
+ 885   # cursor-word
+ 886   var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 887   var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
+ 888   var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
+ 889   var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
+ 890   var cursor-word/ebx: (addr word) <- copy _cursor-word
+ 891   # cursor-call-path
+ 892   var cursor-call-path: (addr handle call-path-element)
+ 893   {
+ 894     var src/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
+ 895     copy-to cursor-call-path, src
+ 896   }
+ 897   # first line
+ 898   var first-line-ah/eax: (addr handle line) <- get sandbox, data
+ 899   var _first-line/eax: (addr line) <- lookup *first-line-ah
+ 900   var first-line/edx: (addr line) <- copy _first-line
+ 901   # final line
+ 902   var final-line-storage: (handle line)
+ 903   var final-line-ah/eax: (addr handle line) <- address final-line-storage
+ 904   final-line sandbox, final-line-ah
+ 905   var final-line/eax: (addr line) <- lookup *final-line-ah
+ 906   # curr-path
+ 907   var curr-path-storage: (handle call-path-element)
+ 908   var curr-path/ecx: (addr handle call-path-element) <- address curr-path-storage
+ 909   allocate curr-path  # leak
+ 910   initialize-path-from-line final-line, curr-path
+ 911   #
+ 912   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
+ 913 }
+ 914 
+ 915 fn final-line _sandbox: (addr sandbox), out: (addr handle line) {
+ 916   var sandbox/esi: (addr sandbox) <- copy _sandbox
+ 917   var curr-line-ah/ecx: (addr handle line) <- get sandbox, data
+ 918   {
+ 919     var curr-line/eax: (addr line) <- lookup *curr-line-ah
+ 920     var next-line-ah/edx: (addr handle line) <- get curr-line, next
+ 921     var next-line/eax: (addr line) <- lookup *next-line-ah
+ 922     compare next-line, 0
+ 923     break-if-=
+ 924     curr-line-ah <- copy next-line-ah
+ 925     loop
+ 926   }
+ 927   copy-object curr-line-ah, out
+ 928 }
+ 929 
+ 930 fn render-rename-dialog screen: (addr screen), _sandbox: (addr sandbox), cursor-row: int, cursor-col: int {
+ 931   var sandbox/edi: (addr sandbox) <- copy _sandbox
+ 932   var rename-word-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-cursor-word
+ 933   var rename-word-mode?/eax: (addr word) <- lookup *rename-word-mode-ah?
+ 934   compare rename-word-mode?, 0
+ 935   break-if-=
+ 936   # clear a space for the dialog
+ 937   var top-row/eax: int <- copy cursor-row
+ 938   top-row <- subtract 3
+ 939   var bottom-row/ecx: int <- copy cursor-row
+ 940   bottom-row <- add 3
+ 941   var left-col/edx: int <- copy cursor-col
+ 942   left-col <- subtract 0x10
+ 943   var right-col/ebx: int <- copy cursor-col
+ 944   right-col <- add 0x10
+ 945   clear-rect screen, top-row, left-col, bottom-row, right-col
+ 946   draw-box screen, top-row, left-col, bottom-row, right-col
+ 947   # render a little menu for the dialog
+ 948   var menu-row/ecx: int <- copy bottom-row
+ 949   menu-row <- decrement
+ 950   var menu-col/edx: int <- copy left-col
+ 951   menu-col <- add 2
+ 952   move-cursor screen, menu-row, menu-col
+ 953   start-reverse-video screen
+ 954   print-string screen, " esc "
+ 955   reset-formatting screen
+ 956   print-string screen, " cancel  "
+ 957   start-reverse-video screen
+ 958   print-string screen, " enter "
+ 959   reset-formatting screen
+ 960   print-string screen, " rename  "
+ 961   # draw the word, positioned appropriately around the cursor
+ 962   var start-col/ecx: int <- copy cursor-col
+ 963   var word-ah?/edx: (addr handle word) <- get sandbox, partial-name-for-cursor-word
+ 964   var word/eax: (addr word) <- lookup *word-ah?
+ 965   var cursor-index/eax: int <- cursor-index word
+ 966   start-col <- subtract cursor-index
+ 967   move-cursor screen, cursor-row, start-col
+ 968   var word/eax: (addr word) <- lookup *word-ah?
+ 969   print-word screen, word
+ 970 }
+ 971 
+ 972 fn render-define-dialog screen: (addr screen), _sandbox: (addr sandbox), cursor-row: int, cursor-col: int {
+ 973   var sandbox/edi: (addr sandbox) <- copy _sandbox
+ 974   var define-function-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-function
+ 975   var define-function-mode?/eax: (addr word) <- lookup *define-function-mode-ah?
+ 976   compare define-function-mode?, 0
+ 977   break-if-=
+ 978   # clear a space for the dialog
+ 979   var top-row/eax: int <- copy cursor-row
+ 980   top-row <- subtract 3
+ 981   var bottom-row/ecx: int <- copy cursor-row
+ 982   bottom-row <- add 3
+ 983   var left-col/edx: int <- copy cursor-col
+ 984   left-col <- subtract 0x10
+ 985   var right-col/ebx: int <- copy cursor-col
+ 986   right-col <- add 0x10
+ 987   clear-rect screen, top-row, left-col, bottom-row, right-col
+ 988   draw-box screen, top-row, left-col, bottom-row, right-col
+ 989   # render a little menu for the dialog
+ 990   var menu-row/ecx: int <- copy bottom-row
+ 991   menu-row <- decrement
+ 992   var menu-col/edx: int <- copy left-col
+ 993   menu-col <- add 2
+ 994   move-cursor screen, menu-row, menu-col
+ 995   start-reverse-video screen
+ 996   print-string screen, " esc "
+ 997   reset-formatting screen
+ 998   print-string screen, " cancel  "
+ 999   start-reverse-video screen
+1000   print-string screen, " enter "
+1001   reset-formatting screen
+1002   print-string screen, " define  "
+1003   # draw the word, positioned appropriately around the cursor
+1004   var start-col/ecx: int <- copy cursor-col
+1005   var word-ah?/edx: (addr handle word) <- get sandbox, partial-name-for-function
+1006   var word/eax: (addr word) <- lookup *word-ah?
+1007   var cursor-index/eax: int <- cursor-index word
+1008   start-col <- subtract cursor-index
+1009   move-cursor screen, cursor-row, start-col
+1010   var word/eax: (addr word) <- lookup *word-ah?
+1011   print-word screen, word
+1012 }
+1013 
+1014 # Render just the words in 'line'.
+1015 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) {
+1016   # curr-word
+1017   var line/eax: (addr line) <- copy _line
+1018   var first-word-ah/eax: (addr handle word) <- get line, data
+1019   var _curr-word/eax: (addr word) <- lookup *first-word-ah
+1020   var curr-word/esi: (addr word) <- copy _curr-word
+1021   #
+1022   # loop-carried dependency
+1023   var curr-col/ecx: int <- copy left-col
+1024   #
+1025   {
+1026     compare curr-word, 0
+1027     break-if-=
+1028 #?     print-string 0, "-- word in penultimate lines: "
+1029 #?     {
+1030 #?       var foo/eax: int <- copy curr-word
+1031 #?       print-int32-hex 0, foo
+1032 #?     }
+1033 #?     print-string 0, "\n"
+1034     var old-col/edx: int <- copy curr-col
+1035     reset-formatting screen
+1036     move-cursor screen, curr-row, curr-col
+1037     print-word screen, curr-word
+1038     {
+1039       var max-width/eax: int <- word-length curr-word
+1040       curr-col <- add max-width
+1041       curr-col <- add 1  # margin-right
+1042     }
+1043     # cache cursor column if necessary
+1044     {
+1045       compare curr-word, cursor-word
+1046       break-if-!=
+1047 #?       print-string 0, "Cursor at "
+1048 #?       print-int32-decimal 0, curr-row
+1049 #?       print-string 0, ", "
+1050 #?       print-int32-decimal 0, old-col
+1051 #?       print-string 0, "\n"
+1052 #?       print-string 0, "contents: "
+1053 #?       print-word 0, cursor-word
+1054 #?       print-string 0, "\n"
+1055 #?       {
+1056 #?         var foo/eax: int <- copy cursor-word
+1057 #?         print-int32-hex 0, foo
+1058 #?         print-string 0, "\n"
+1059 #?       }
+1060       var dest/ecx: (addr int) <- copy cursor-row-addr
+1061       var src/eax: int <- copy curr-row
+1062       copy-to *dest, src
+1063       dest <- copy cursor-col-addr
+1064       copy-to *dest, old-col
+1065       var cursor-index-in-word/eax: int <- cursor-index curr-word
+1066       add-to *dest, cursor-index-in-word
+1067     }
+1068     # loop update
+1069     var next-word-ah/edx: (addr handle word) <- get curr-word, next
+1070     var _curr-word/eax: (addr word) <- lookup *next-word-ah
+1071     curr-word <- copy _curr-word
+1072     loop
+1073   }
+1074 }
+1075 
+1076 fn call-depth-at-cursor _sandbox: (addr sandbox) -> result/eax: int {
+1077   var sandbox/esi: (addr sandbox) <- copy _sandbox
+1078   var cursor-call-path/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path
+1079   result <- call-path-element-length cursor-call-path
+1080   result <- add 2  # input-row-1
+1081 }
+1082 
+1083 fn call-path-element-length _x: (addr handle call-path-element) -> result/eax: int {
+1084   var curr-ah/ecx: (addr handle call-path-element) <- copy _x
+1085   var out/edi: int <- copy 0
+1086   {
+1087     var curr/eax: (addr call-path-element) <- lookup *curr-ah
+1088     compare curr, 0
+1089     break-if-=
+1090     curr-ah <- get curr, next
+1091     out <- increment
+1092     loop
+1093   }
+1094   result <- copy out
+1095 }
+1096 
+1097 # Render the line of words in line, along with the state of the stack under each word.
+1098 # Also render any expanded function calls using recursive calls.
+1099 #
+1100 # Along the way, compute the column the cursor should be positioned at (cursor-col-addr).
+1101 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) -> right-col/ecx: int {
+1102 #?   print-string 0, "--\n"
+1103   # curr-word
+1104   var line/esi: (addr line) <- copy _line
+1105   var first-word-ah/eax: (addr handle word) <- get line, data
+1106   var curr-word/eax: (addr word) <- lookup *first-word-ah
+1107   var debug-row: int
+1108   copy-to debug-row, 0x20
+1109   #
+1110   # loop-carried dependency
+1111   var curr-col/ecx: int <- copy left-col
+1112   #
+1113   {
+1114     compare curr-word, 0
+1115     break-if-=
+1116 #?     print-string 0, "-- word in final line: "
+1117 #?     {
+1118 #?       var foo/eax: int <- copy curr-word
+1119 #?       print-int32-hex 0, foo
+1120 #?     }
+1121 #?     print-string 0, "\n"
+1122     # if necessary, first render columns for subsidiary stack
+1123     $render-line:subsidiary: {
+1124       {
+1125 #?         print-string 0, "check sub\n"
+1126         var display-subsidiary-stack?/eax: boolean <- find-in-call-paths expanded-words, curr-path
+1127         compare display-subsidiary-stack?, 0  # false
+1128         break-if-= $render-line:subsidiary
+1129       }
+1130 #?       print-string 0, "render subsidiary stack\n"
+1131       # does function exist?
+1132       var callee/edi: (addr function) <- copy 0
+1133       {
+1134         var callee-h: (handle function)
+1135         var callee-ah/ecx: (addr handle function) <- address callee-h
+1136         callee functions, curr-word, callee-ah
+1137         var _callee/eax: (addr function) <- lookup *callee-ah
+1138         callee <- copy _callee
+1139         compare callee, 0
+1140         break-if-= $render-line:subsidiary
+1141       }
+1142       move-cursor screen, top-row, curr-col
+1143       start-color screen, 8, 7
+1144       print-word screen, curr-word
+1145       {
+1146         var word-len/eax: int <- word-length curr-word
+1147         curr-col <- add word-len
+1148         curr-col <- add 2
+1149         increment top-row
+1150       }
+1151       # obtain stack at call site
+1152       var stack-storage: value-stack
+1153       var stack/edx: (addr value-stack) <- address stack-storage
+1154       initialize-value-stack stack, 0x10
+1155       {
+1156         var prev-word-ah/eax: (addr handle word) <- get curr-word, prev
+1157         var prev-word/eax: (addr word) <- lookup *prev-word-ah
+1158         compare prev-word, 0
+1159         break-if-=
+1160         evaluate functions, bindings, line, prev-word, stack
+1161       }
+1162       # construct new bindings
+1163       var callee-bindings-storage: table
+1164       var callee-bindings/esi: (addr table) <- address callee-bindings-storage
+1165       initialize-table callee-bindings, 0x10
+1166       bind-args callee, stack, callee-bindings
+1167       # obtain body
+1168       var callee-body-ah/eax: (addr handle line) <- get callee, body
+1169       var callee-body/eax: (addr line) <- lookup *callee-body-ah
+1170       var callee-body-first-word/edx: (addr handle word) <- get callee-body, data
+1171       # - render subsidiary stack
+1172       push-to-call-path-element curr-path, callee-body-first-word  # leak
+1173       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
+1174       drop-from-call-path-element curr-path
+1175       #
+1176       move-cursor screen, top-row, curr-col
+1177       print-code-point screen, 0x21d7  # ⇗
+1178       #
+1179       curr-col <- add 2
+1180       decrement top-row
+1181     }
+1182     # render main column
+1183     var old-col/edx: int <- copy curr-col
+1184 #?     move-cursor 0, debug-row, 1
+1185 #?     increment debug-row
+1186 #?     print-string 0, "rendering column from "
+1187 #?     print-int32-decimal 0, curr-col
+1188 #?     print-string 0, "\n"
+1189     curr-col <- render-column screen, functions, bindings, first-line, line, curr-word, top-row, curr-col
+1190     # cache cursor column if necessary
+1191     $render-line:cache-cursor-column: {
+1192 #?       print-string 0, "cache cursor? "
+1193 #?       {
+1194 #?         var foo/eax: int <- copy curr-word
+1195 #?         print-int32-hex 0, foo
+1196 #?       }
+1197 #?       print-string 0, "\n"
+1198       {
+1199         var found?/eax: boolean <- call-path-element-match? curr-path, cursor-call-path
+1200         compare found?, 0  # false
+1201         break-if-= $render-line:cache-cursor-column
+1202       }
+1203 #?       print-string 0, "cursor at "
+1204 #?       print-int32-decimal 0, top-row
+1205 #?       print-string 0, ", "
+1206 #?       print-int32-decimal 0, old-col
+1207 #?       print-string 0, "\n"
+1208       var dest/edi: (addr int) <- copy cursor-row-addr
+1209       {
+1210         var src/eax: int <- copy top-row
+1211         copy-to *dest, src
+1212       }
+1213       dest <- copy cursor-col-addr
+1214       copy-to *dest, old-col
+1215       var cursor-index-in-word/eax: int <- cursor-index curr-word
+1216       add-to *dest, cursor-index-in-word
+1217     }
+1218     # loop update
+1219 #?     print-string 0, "next word\n"
+1220     var next-word-ah/edx: (addr handle word) <- get curr-word, next
+1221     curr-word <- lookup *next-word-ah
+1222 #?     {
+1223 #?       var foo/eax: int <- copy curr-word
+1224 #?       print-int32-hex 0, foo
+1225 #?       print-string 0, "\n"
+1226 #?     }
+1227     increment-final-element curr-path
+1228     loop
+1229   }
+1230   right-col <- copy curr-col
+1231 }
+1232 
+1233 fn callee functions: (addr handle function), word: (addr word), out: (addr handle function) {
+1234   var stream-storage: (stream byte 0x10)
+1235   var stream/esi: (addr stream byte) <- address stream-storage
+1236   emit-word word, stream
+1237   find-function functions, stream, out
+1238 }
+1239 
+1240 # Render:
+1241 #   - starting at top-row, left-col: final-word
+1242 #   - starting somewhere below at left-col: the stack result from interpreting first-world to final-word (inclusive)
+1243 #
+1244 # Return the farthest column written.
+1245 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 -> right-col/ecx: int {
+1246 #?   print-string 0, "render-column\n"
+1247   var max-width/esi: int <- copy 0
+1248   {
+1249     # indent stack
+1250     var indented-col/ebx: int <- copy left-col
+1251     indented-col <- add 1  # margin-right
+1252     # compute stack
+1253     var stack: value-stack
+1254     var stack-addr/edi: (addr value-stack) <- address stack
+1255     initialize-value-stack stack-addr, 0x10  # max-words
+1256     evaluate functions, bindings, first-line, final-word, stack-addr
+1257     # render stack
+1258     var curr-row/edx: int <- copy top-row
+1259     curr-row <- add 3  # stack-margin-top
+1260     var _max-width/eax: int <- value-stack-max-width stack-addr
+1261     max-width <- copy _max-width
+1262     var i/eax: int <- value-stack-length stack-addr
+1263     {
+1264       compare i, 0
+1265       break-if-<=
+1266       move-cursor screen, curr-row, indented-col
+1267       {
+1268         var val/eax: int <- pop-int-from-value-stack stack-addr
+1269 #?         print-int32-decimal 0, val
+1270 #?         print-string 0, "\n"
+1271         render-integer screen, val, max-width
+1272       }
+1273       curr-row <- increment
+1274       i <- decrement
+1275       loop
+1276     }
+1277   }
+1278 
+1279   max-width <- add 2  # spaces on either side of items on the stack
+1280 
+1281   # render word, initialize result
+1282   reset-formatting screen
+1283   move-cursor screen, top-row, left-col
+1284   print-word screen, final-word
+1285   {
+1286     var size/eax: int <- word-length final-word
+1287     compare size, max-width
+1288     break-if-<=
+1289     max-width <- copy size
+1290   }
+1291 
+1292   # post-process right-col
+1293   right-col <- copy left-col
+1294   right-col <- add max-width
+1295   right-col <- add 1  # margin-right
+1296 #?   print-int32-decimal 0, left-col
+1297 #?   print-string 0, " => "
+1298 #?   print-int32-decimal 0, right-col
+1299 #?   print-string 0, "\n"
+1300 }
+1301 
+1302 # synaesthesia
+1303 fn render-integer screen: (addr screen), val: int, max-width: int {
+1304   var bg/eax: int <- hash-color val
+1305   var fg/ecx: int <- copy 7
+1306   {
+1307     compare bg, 2
+1308     break-if-!=
+1309     fg <- copy 0
+1310   }
+1311   {
+1312     compare bg, 3
+1313     break-if-!=
+1314     fg <- copy 0
+1315   }
+1316   {
+1317     compare bg, 6
+1318     break-if-!=
+1319     fg <- copy 0
+1320   }
+1321   start-color screen, fg, bg
+1322   print-grapheme screen, 0x20  # space
+1323   print-int32-decimal-right-justified screen, val, max-width
+1324   print-grapheme screen, 0x20  # space
+1325 }
+1326 
+1327 fn hash-color val: int -> result/eax: int {
+1328   result <- try-modulo val, 7  # assumes that 7 is always the background color
+1329 }
+1330 
+1331 fn clear-canvas _env: (addr environment) {
+1332   var env/esi: (addr environment) <- copy _env
+1333   var screen-ah/edi: (addr handle screen) <- get env, screen
+1334   var _screen/eax: (addr screen) <- lookup *screen-ah
+1335   var screen/edi: (addr screen) <- copy _screen
+1336   clear-screen screen
+1337   var nrows/eax: (addr int) <- get env, nrows
+1338   var _repl-col/ecx: (addr int) <- get env, code-separator-col
+1339   var repl-col/ecx: int <- copy *_repl-col
+1340   draw-vertical-line screen, 1, *nrows, repl-col
+1341   # wordstar-style cheatsheet of shortcuts
+1342   move-cursor screen, *nrows, 0
+1343   start-reverse-video screen
+1344   print-string screen, " ctrl-q "
+1345   reset-formatting screen
+1346   print-string screen, " quit "
+1347   var menu-start/ecx: int <- copy repl-col
+1348   menu-start <- subtract 0x40  # 64 = half the size of the menu
+1349   move-cursor screen, *nrows, menu-start
+1350   start-reverse-video screen
+1351   print-string screen, " ctrl-a "
+1352   reset-formatting screen
+1353   print-string screen, " ⏮   "
+1354   start-reverse-video screen
+1355   print-string screen, " ctrl-b "
+1356   reset-formatting screen
+1357   print-string screen, " ◀ word  "
+1358   start-reverse-video screen
+1359   print-string screen, " ctrl-f "
+1360   reset-formatting screen
+1361   print-string screen, " word ▶  "
+1362   start-reverse-video screen
+1363   print-string screen, " ctrl-e "
+1364   reset-formatting screen
+1365   print-string screen, " ⏭   "
+1366   start-reverse-video screen
+1367   print-string screen, " ctrl-u "
+1368   reset-formatting screen
+1369   print-string screen, " clear line  "
+1370   start-reverse-video screen
+1371   print-string screen, " ctrl-n "
+1372   reset-formatting screen
+1373   print-string screen, " name value  "
+1374   start-reverse-video screen
+1375   print-string screen, " ctrl-d "
+1376   reset-formatting screen
+1377   print-string screen, " define function  "
+1378   # currently defined functions
+1379   var row/ecx: int <- copy 3
+1380   var functions/esi: (addr handle function) <- get env, functions
+1381   {
+1382     var curr/eax: (addr function) <- lookup *functions
+1383     compare curr, 0
+1384     break-if-=
+1385     move-cursor screen, row, 2
+1386     render-function screen, curr
+1387     functions <- get curr, next
+1388     row <- increment
+1389     loop
+1390   }
+1391 }
+1392 
+1393 # only single-line functions supported for now
+1394 fn render-function screen: (addr screen), _f: (addr function) {
+1395   var f/esi: (addr function) <- copy _f
+1396   var args/ecx: (addr handle word) <- get f, args
+1397   print-words-in-reverse screen, args
+1398   var name-ah/eax: (addr handle array byte) <- get f, name
+1399   var name/eax: (addr array byte) <- lookup *name-ah
+1400   print-string screen, name
+1401   print-string screen, " = "
+1402   var body-ah/eax: (addr handle line) <- get f, body
+1403   var body/eax: (addr line) <- lookup *body-ah
+1404   var body-words-ah/eax: (addr handle word) <- get body, data
+1405   print-words screen, body-words-ah
+1406 }
+1407 
+1408 fn real-grapheme? g: grapheme -> result/eax: boolean {
+1409 $real-grapheme?:body: {
+1410   # if g == newline return true
+1411   compare g, 0xa
+1412   {
+1413     break-if-!=
+1414     result <- copy 1  # true
+1415     break $real-grapheme?:body
+1416   }
+1417   # if g == tab return true
+1418   compare g, 9
+1419   {
+1420     break-if-!=
+1421     result <- copy 1  # true
+1422     break $real-grapheme?:body
+1423   }
+1424   # if g < 32 return false
+1425   compare g, 0x20
+1426   {
+1427     break-if->=
+1428     result <- copy 0  # false
+1429     break $real-grapheme?:body
+1430   }
+1431   # if g <= 255 return true
+1432   compare g, 0xff
+1433   {
+1434     break-if->
+1435     result <- copy 1  # true
+1436     break $real-grapheme?:body
+1437   }
+1438   # if (g&0xff == Esc) it's an escape sequence
+1439   and-with g, 0xff
+1440   compare g, 0x1b  # Esc
+1441   {
+1442     break-if-!=
+1443     result <- copy 0  # false
+1444     break $real-grapheme?:body
+1445   }
+1446   # otherwise return true
+1447   result <- copy 1  # true
+1448 }
+1449 }
 
diff --git a/html/apps/tile/gap-buffer.mu.html b/html/apps/tile/gap-buffer.mu.html index 02c71b8f..886487ed 100644 --- a/html/apps/tile/gap-buffer.mu.html +++ b/html/apps/tile/gap-buffer.mu.html @@ -347,6 +347,34 @@ if ('onhashchange' in window) { 288 var result/eax: int <- copy _result 289 check-ints-equal result, 1, "F - test-gap-buffer-equal-from-start?" 290 } +291 +292 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) { +293 # obtain src-a, dest-a +294 var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah +295 var _src-a/eax: (addr gap-buffer) <- lookup *src-ah +296 var src-a/esi: (addr gap-buffer) <- copy _src-a +297 var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah +298 var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah +299 var dest-a/edi: (addr gap-buffer) <- copy _dest-a +300 # copy left grapheme-stack +301 var src/ecx: (addr grapheme-stack) <- get src-a, left +302 var dest/edx: (addr grapheme-stack) <- get dest-a, left +303 copy-grapheme-stack src, dest +304 # copy right grapheme-stack +305 src <- get src-a, right +306 dest <- get dest-a, right +307 copy-grapheme-stack src, dest +308 } +309 +310 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> result/eax: boolean { +311 var self/esi: (addr gap-buffer) <- copy _self +312 var curr/ecx: (addr grapheme-stack) <- get self, left +313 result <- grapheme-stack-is-decimal-integer? curr +314 compare result, 0 # false +315 break-if-= +316 curr <- get self, right +317 result <- grapheme-stack-is-decimal-integer? curr +318 } diff --git a/html/apps/tile/grapheme-stack.mu.html b/html/apps/tile/grapheme-stack.mu.html index 32f7aa0b..38285b25 100644 --- a/html/apps/tile/grapheme-stack.mu.html +++ b/html/apps/tile/grapheme-stack.mu.html @@ -69,152 +69,194 @@ if ('onhashchange' in window) { 11 copy-to *top, 0 12 } 13 - 14 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> result/eax: boolean { - 15 $grapheme-stack-empty?:body: { - 16 var self/esi: (addr grapheme-stack) <- copy _self - 17 var top/eax: (addr int) <- get self, top - 18 compare *top, 0 - 19 { - 20 break-if-= - 21 result <- copy 1 # false - 22 break $grapheme-stack-empty?:body - 23 } - 24 result <- copy 0 # false - 25 } - 26 } - 27 - 28 fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme { - 29 var self/esi: (addr grapheme-stack) <- copy _self - 30 var top-addr/ecx: (addr int) <- get self, top - 31 var data-ah/edx: (addr handle array grapheme) <- get self, data - 32 var data/eax: (addr array grapheme) <- lookup *data-ah - 33 var top/edx: int <- copy *top-addr - 34 var dest-addr/edx: (addr grapheme) <- index data, top - 35 var val/eax: grapheme <- copy _val - 36 copy-to *dest-addr, val - 37 add-to *top-addr, 1 - 38 } - 39 - 40 fn pop-grapheme-stack _self: (addr grapheme-stack) -> val/eax: grapheme { - 41 $pop-grapheme-stack:body: { - 42 var self/esi: (addr grapheme-stack) <- copy _self - 43 var top-addr/ecx: (addr int) <- get self, top - 44 { - 45 compare *top-addr, 0 - 46 break-if-> - 47 val <- copy -1 - 48 break $pop-grapheme-stack:body - 49 } - 50 subtract-from *top-addr, 1 - 51 var data-ah/edx: (addr handle array grapheme) <- get self, data - 52 var data/eax: (addr array grapheme) <- lookup *data-ah - 53 var top/edx: int <- copy *top-addr - 54 var result-addr/eax: (addr grapheme) <- index data, top - 55 val <- copy *result-addr - 56 } - 57 } - 58 - 59 # dump stack to screen from bottom to top - 60 # don't move the cursor or anything - 61 fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { - 62 var self/esi: (addr grapheme-stack) <- copy _self - 63 var data-ah/edi: (addr handle array grapheme) <- get self, data - 64 var _data/eax: (addr array grapheme) <- lookup *data-ah - 65 var data/edi: (addr array grapheme) <- copy _data - 66 var top-addr/ecx: (addr int) <- get self, top - 67 var i/eax: int <- copy 0 - 68 { - 69 compare i, *top-addr - 70 break-if->= - 71 var g/edx: (addr grapheme) <- index data, i - 72 print-grapheme screen, *g - 73 i <- increment - 74 loop - 75 } - 76 } - 77 - 78 # dump stack to screen from top to bottom - 79 # don't move the cursor or anything - 80 fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { - 81 var self/esi: (addr grapheme-stack) <- copy _self - 82 var data-ah/edi: (addr handle array grapheme) <- get self, data - 83 var _data/eax: (addr array grapheme) <- lookup *data-ah - 84 var data/edi: (addr array grapheme) <- copy _data - 85 var top-addr/ecx: (addr int) <- get self, top - 86 var i/eax: int <- copy *top-addr - 87 i <- decrement - 88 { - 89 compare i, 0 - 90 break-if-< - 91 var g/edx: (addr grapheme) <- index data, i - 92 print-grapheme screen, *g - 93 i <- decrement - 94 loop - 95 } - 96 } - 97 - 98 # compare from bottom - 99 # beware: modifies 'stream', which must be disposed of after a false result -100 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> result/eax: boolean { -101 $prefix-match?:body: { -102 var self/esi: (addr grapheme-stack) <- copy _self -103 var data-ah/edi: (addr handle array grapheme) <- get self, data -104 var _data/eax: (addr array grapheme) <- lookup *data-ah -105 var data/edi: (addr array grapheme) <- copy _data -106 var top-addr/ecx: (addr int) <- get self, top -107 var i/ebx: int <- copy 0 -108 { -109 compare i, *top-addr -110 break-if->= -111 # if curr != expected, return false -112 { -113 var curr-a/edx: (addr grapheme) <- index data, i -114 var expected/eax: grapheme <- read-grapheme s -115 { -116 compare expected, *curr-a -117 break-if-= -118 result <- copy 0 # false -119 break $prefix-match?:body -120 } -121 } -122 i <- increment -123 loop -124 } -125 result <- copy 1 # true -126 } -127 } -128 -129 # compare from bottom -130 # beware: modifies 'stream', which must be disposed of after a false result -131 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> result/eax: boolean { -132 $suffix-match?:body: { -133 var self/esi: (addr grapheme-stack) <- copy _self -134 var data-ah/edi: (addr handle array grapheme) <- get self, data -135 var _data/eax: (addr array grapheme) <- lookup *data-ah -136 var data/edi: (addr array grapheme) <- copy _data -137 var top-addr/eax: (addr int) <- get self, top -138 var i/ebx: int <- copy *top-addr -139 i <- decrement -140 { -141 compare i, 0 -142 break-if-< -143 { -144 var curr-a/edx: (addr grapheme) <- index data, i -145 var expected/eax: grapheme <- read-grapheme s -146 # if curr != expected, return false -147 { -148 compare expected, *curr-a -149 break-if-= -150 result <- copy 0 # false -151 break $suffix-match?:body -152 } -153 } -154 i <- decrement -155 loop -156 } -157 result <- copy 1 # true -158 } -159 } + 14 fn clear-grapheme-stack _self: (addr grapheme-stack) { + 15 var self/esi: (addr grapheme-stack) <- copy _self + 16 var top/eax: (addr int) <- get self, top + 17 copy-to *top, 0 + 18 } + 19 + 20 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> result/eax: boolean { + 21 $grapheme-stack-empty?:body: { + 22 var self/esi: (addr grapheme-stack) <- copy _self + 23 var top/eax: (addr int) <- get self, top + 24 compare *top, 0 + 25 { + 26 break-if-!= + 27 result <- copy 1 # true + 28 break $grapheme-stack-empty?:body + 29 } + 30 result <- copy 0 # false + 31 } + 32 } + 33 + 34 fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme { + 35 var self/esi: (addr grapheme-stack) <- copy _self + 36 var top-addr/ecx: (addr int) <- get self, top + 37 var data-ah/edx: (addr handle array grapheme) <- get self, data + 38 var data/eax: (addr array grapheme) <- lookup *data-ah + 39 var top/edx: int <- copy *top-addr + 40 var dest-addr/edx: (addr grapheme) <- index data, top + 41 var val/eax: grapheme <- copy _val + 42 copy-to *dest-addr, val + 43 add-to *top-addr, 1 + 44 } + 45 + 46 fn pop-grapheme-stack _self: (addr grapheme-stack) -> val/eax: grapheme { + 47 $pop-grapheme-stack:body: { + 48 var self/esi: (addr grapheme-stack) <- copy _self + 49 var top-addr/ecx: (addr int) <- get self, top + 50 { + 51 compare *top-addr, 0 + 52 break-if-> + 53 val <- copy -1 + 54 break $pop-grapheme-stack:body + 55 } + 56 subtract-from *top-addr, 1 + 57 var data-ah/edx: (addr handle array grapheme) <- get self, data + 58 var data/eax: (addr array grapheme) <- lookup *data-ah + 59 var top/edx: int <- copy *top-addr + 60 var result-addr/eax: (addr grapheme) <- index data, top + 61 val <- copy *result-addr + 62 } + 63 } + 64 + 65 fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) { + 66 var src/esi: (addr grapheme-stack) <- copy _src + 67 var data-ah/edi: (addr handle array grapheme) <- get src, data + 68 var _data/eax: (addr array grapheme) <- lookup *data-ah + 69 var data/edi: (addr array grapheme) <- copy _data + 70 var top-addr/ecx: (addr int) <- get src, top + 71 var i/eax: int <- copy 0 + 72 { + 73 compare i, *top-addr + 74 break-if->= + 75 var g/edx: (addr grapheme) <- index data, i + 76 push-grapheme-stack dest, *g + 77 i <- increment + 78 loop + 79 } + 80 } + 81 + 82 # dump stack to screen from bottom to top + 83 # don't move the cursor or anything + 84 fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { + 85 var self/esi: (addr grapheme-stack) <- copy _self + 86 var data-ah/edi: (addr handle array grapheme) <- get self, data + 87 var _data/eax: (addr array grapheme) <- lookup *data-ah + 88 var data/edi: (addr array grapheme) <- copy _data + 89 var top-addr/ecx: (addr int) <- get self, top + 90 var i/eax: int <- copy 0 + 91 { + 92 compare i, *top-addr + 93 break-if->= + 94 var g/edx: (addr grapheme) <- index data, i + 95 print-grapheme screen, *g + 96 i <- increment + 97 loop + 98 } + 99 } +100 +101 # dump stack to screen from top to bottom +102 # don't move the cursor or anything +103 fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { +104 var self/esi: (addr grapheme-stack) <- copy _self +105 var data-ah/edi: (addr handle array grapheme) <- get self, data +106 var _data/eax: (addr array grapheme) <- lookup *data-ah +107 var data/edi: (addr array grapheme) <- copy _data +108 var top-addr/ecx: (addr int) <- get self, top +109 var i/eax: int <- copy *top-addr +110 i <- decrement +111 { +112 compare i, 0 +113 break-if-< +114 var g/edx: (addr grapheme) <- index data, i +115 print-grapheme screen, *g +116 i <- decrement +117 loop +118 } +119 } +120 +121 # compare from bottom +122 # beware: modifies 'stream', which must be disposed of after a false result +123 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> result/eax: boolean { +124 $prefix-match?:body: { +125 var self/esi: (addr grapheme-stack) <- copy _self +126 var data-ah/edi: (addr handle array grapheme) <- get self, data +127 var _data/eax: (addr array grapheme) <- lookup *data-ah +128 var data/edi: (addr array grapheme) <- copy _data +129 var top-addr/ecx: (addr int) <- get self, top +130 var i/ebx: int <- copy 0 +131 { +132 compare i, *top-addr +133 break-if->= +134 # if curr != expected, return false +135 { +136 var curr-a/edx: (addr grapheme) <- index data, i +137 var expected/eax: grapheme <- read-grapheme s +138 { +139 compare expected, *curr-a +140 break-if-= +141 result <- copy 0 # false +142 break $prefix-match?:body +143 } +144 } +145 i <- increment +146 loop +147 } +148 result <- copy 1 # true +149 } +150 } +151 +152 # compare from bottom +153 # beware: modifies 'stream', which must be disposed of after a false result +154 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> result/eax: boolean { +155 $suffix-match?:body: { +156 var self/esi: (addr grapheme-stack) <- copy _self +157 var data-ah/edi: (addr handle array grapheme) <- get self, data +158 var _data/eax: (addr array grapheme) <- lookup *data-ah +159 var data/edi: (addr array grapheme) <- copy _data +160 var top-addr/eax: (addr int) <- get self, top +161 var i/ebx: int <- copy *top-addr +162 i <- decrement +163 { +164 compare i, 0 +165 break-if-< +166 { +167 var curr-a/edx: (addr grapheme) <- index data, i +168 var expected/eax: grapheme <- read-grapheme s +169 # if curr != expected, return false +170 { +171 compare expected, *curr-a +172 break-if-= +173 result <- copy 0 # false +174 break $suffix-match?:body +175 } +176 } +177 i <- decrement +178 loop +179 } +180 result <- copy 1 # true +181 } +182 } +183 +184 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> result/eax: boolean { +185 var self/esi: (addr grapheme-stack) <- copy _self +186 var data-ah/eax: (addr handle array grapheme) <- get self, data +187 var _data/eax: (addr array grapheme) <- lookup *data-ah +188 var data/edx: (addr array grapheme) <- copy _data +189 var top-addr/ecx: (addr int) <- get self, top +190 var i/ebx: int <- copy 0 +191 $grapheme-stack-is-integer?:loop: { +192 compare i, *top-addr +193 break-if->= +194 var g/edx: (addr grapheme) <- index data, i +195 result <- is-decimal-digit? *g +196 compare result, 0 # false +197 break-if-= +198 i <- increment +199 loop +200 } +201 } diff --git a/html/apps/tile/int-stack.mu.html b/html/apps/tile/int-stack.mu.html index 13b66b74..5298b78d 100644 --- a/html/apps/tile/int-stack.mu.html +++ b/html/apps/tile/int-stack.mu.html @@ -125,29 +125,6 @@ if ('onhashchange' in window) { 67 var top-addr/eax: (addr int) <- get self, top 68 result <- copy *top-addr 69 } -70 -71 fn int-stack-max-width _self: (addr int-stack) -> result/eax: int { -72 var self/esi: (addr int-stack) <- copy _self -73 var data-ah/edi: (addr handle array int) <- get self, data -74 var _data/eax: (addr array int) <- lookup *data-ah -75 var data/edi: (addr array int) <- copy _data -76 var top-addr/ecx: (addr int) <- get self, top -77 var i/ebx: int <- copy 0 -78 result <- copy 0 -79 { -80 compare i, *top-addr -81 break-if->= -82 var g/edx: (addr int) <- index data, i -83 var w/ecx: int <- int-width-decimal *g -84 compare w, result -85 { -86 break-if-<= -87 result <- copy w -88 } -89 i <- increment -90 loop -91 } -92 } diff --git a/html/apps/tile/main.mu.html b/html/apps/tile/main.mu.html index 8fbd435b..7440bd07 100644 --- a/html/apps/tile/main.mu.html +++ b/html/apps/tile/main.mu.html @@ -14,6 +14,7 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.CommentedCode { color: #8a8a8a; } .muComment { color: #005faf; } .LineNr { } .SpecialChar { color: #d70000; } @@ -78,7 +79,7 @@ if ('onhashchange' in window) { 20 compare tmp2, 0 # false 21 { 22 break-if-= - 23 interactive + 23 interactive 24 exit-status <- copy 0 25 break $main-body 26 } @@ -87,84 +88,101 @@ if ('onhashchange' in window) { 29 compare tmp2, 0 # false 30 { 31 break-if-= - 32 repl + 32 repl 33 exit-status <- copy 0 34 break $main-body 35 } - 36 } - 37 # otherwise error message - 38 print-string-to-real-screen "usage:\n" - 39 print-string-to-real-screen " to run tests: tile test\n" - 40 print-string-to-real-screen " full-screen mode: tile screen\n" - 41 print-string-to-real-screen " regular REPL: tile type\n" - 42 exit-status <- copy 1 - 43 } - 44 } - 45 - 46 fn interactive { - 47 enable-screen-grid-mode - 48 enable-keyboard-immediate-mode - 49 var env-storage: environment - 50 var env/esi: (addr environment) <- address env-storage - 51 initialize-environment env - 52 draw-screen env - 53 { - 54 var key/eax: grapheme <- read-key-from-real-keyboard - 55 compare key, 0x71 # 'q' - 56 break-if-= - 57 process env, key - 58 render env - 59 loop - 60 } - 61 enable-keyboard-type-mode - 62 enable-screen-type-mode - 63 } - 64 - 65 fn repl { - 66 enable-keyboard-immediate-mode - 67 var env-storage: environment - 68 var env/esi: (addr environment) <- address env-storage - 69 initialize-environment env - 70 var stack-storage: value-stack - 71 var stack/edi: (addr value-stack) <- address stack-storage - 72 initialize-value-stack stack, 0x10 - 73 print-string-to-real-screen "> " - 74 $repl:loop: { - 75 var key/eax: grapheme <- read-key-from-real-keyboard - 76 print-grapheme-to-real-screen key - 77 compare key, 4 # ctrl-d - 78 break-if-= - 79 compare key, 0xa # newline - 80 { - 81 break-if-!= - 82 evaluate-environment env, stack - 83 var empty?/eax: boolean <- value-stack-empty? stack - 84 { - 85 compare empty?, 0 # false - 86 break-if-!= - 87 var result/eax: int <- pop-int-from-value-stack stack - 88 print-int32-decimal-to-real-screen result - 89 print-string-to-real-screen "\n" - 90 } - 91 # clear line - 92 var cursor-word-ah/ecx: (addr handle word) <- get env, cursor-word - 93 var program-ah/eax: (addr handle program) <- get env, program - 94 var _program/eax: (addr program) <- lookup *program-ah - 95 var program/esi: (addr program) <- copy _program - 96 var sandbox-ah/esi: (addr handle sandbox) <- get program, sandboxes - 97 var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah - 98 var line-ah/eax: (addr handle line) <- get sandbox, data - 99 var _line/eax: (addr line) <- lookup *line-ah -100 var line/esi: (addr line) <- copy _line -101 initialize-line line, cursor-word-ah -102 print-string-to-real-screen "> " -103 loop $repl:loop -104 } -105 process env, key -106 loop -107 } -108 enable-keyboard-type-mode -109 } + 36 # if single arg is 'test' ... + 37 tmp2 <- string-equal? *tmp, "test2" + 38 compare tmp2, 0 # false + 39 { + 40 break-if-= + 41 test + 42 exit-status <- copy 0 + 43 break $main-body + 44 } + 45 } + 46 # otherwise error message + 47 print-string-to-real-screen "usage:\n" + 48 print-string-to-real-screen " to run tests: tile test\n" + 49 print-string-to-real-screen " full-screen mode: tile screen\n" + 50 print-string-to-real-screen " regular REPL: tile type\n" + 51 exit-status <- copy 1 + 52 } + 53 } + 54 + 55 fn interactive { + 56 enable-screen-grid-mode + 57 enable-keyboard-immediate-mode + 58 var env-storage: environment + 59 var env/esi: (addr environment) <- address env-storage + 60 initialize-environment env + 61 draw-screen env + 62 { + 63 var key/eax: grapheme <- read-key-from-real-keyboard + 64 compare key, 0x11 # 'ctrl-q' + 65 break-if-= + 66 process env, key + 67 render env + 68 loop + 69 } + 70 enable-keyboard-type-mode + 71 enable-screen-type-mode + 72 } + 73 + 74 fn test { + 75 var env-storage: environment + 76 var env/esi: (addr environment) <- address env-storage + 77 initialize-environment-with-fake-screen env, 5, 0xa + 78 var g/eax: grapheme <- copy 0x31 # '1' + 79 process env, g + 80 g <- copy 1 # 'ctrl-a' + 81 process env, g + 82 #? render env + 83 } + 84 + 85 fn repl { + 86 { + 87 # prompt + 88 var line-storage: (stream byte 0x100) + 89 var line/ecx: (addr stream byte) <- address line-storage + 90 print-string-to-real-screen "> " + 91 # read + 92 clear-stream line + 93 read-line-from-real-keyboard line + 94 var done?/eax: boolean <- stream-empty? line + 95 compare done?, 0 # false + 96 break-if-!= + 97 # parse + 98 var env-storage: environment + 99 var env/esi: (addr environment) <- address env-storage +100 initialize-environment env +101 { +102 var done?/eax: boolean <- stream-empty? line +103 compare done?, 0 # false +104 break-if-!= +105 var g/eax: grapheme <- read-grapheme line +106 process env, g +107 loop +108 } +109 # eval +110 var stack-storage: value-stack +111 var stack/edi: (addr value-stack) <- address stack-storage +112 initialize-value-stack stack, 0x10 +113 evaluate-environment env, stack +114 # print +115 var empty?/eax: boolean <- value-stack-empty? stack +116 { +117 compare empty?, 0 # false +118 break-if-!= +119 var result/eax: int <- pop-int-from-value-stack stack +120 print-int32-decimal-to-real-screen result +121 print-string-to-real-screen "\n" +122 } +123 # +124 loop +125 } +126 } diff --git a/html/apps/tile/rpn.mu.html b/html/apps/tile/rpn.mu.html index 39cbea35..d8a4a3e9 100644 --- a/html/apps/tile/rpn.mu.html +++ b/html/apps/tile/rpn.mu.html @@ -20,6 +20,7 @@ a { color:inherit; } .SpecialChar { color: #d70000; } .Delimiter { color: #c000c0; } .muFunction { color: #af5f00; text-decoration: underline; } +.muTest { color: #5f8700; } .Constant { color: #008787; } .PreProc { color: #c000c0; } --> @@ -57,7 +58,7 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/apps/tile/rpn.mu
-  1 fn evaluate defs: (addr handle function), bindings: (addr table), scratch: (addr line), end: (addr word), out: (addr value-stack) {
+  1 fn evaluate functions: (addr handle function), bindings: (addr table), scratch: (addr line), end: (addr word), out: (addr value-stack) {
   2   var line/eax: (addr line) <- copy scratch
   3   var word-ah/eax: (addr handle word) <- get line, data
   4   var curr/eax: (addr word) <- lookup *word-ah
@@ -107,177 +108,221 @@ if ('onhashchange' in window) {
  48         push-int-to-value-stack out, a
  49         break $evaluate:process-word
  50       }
- 51       # if curr-stream is a known function name, call it appropriately
+ 51       # if curr-stream defines a binding, save top of stack to bindings
  52       {
- 53         var callee-h: (handle function)
- 54         var callee-ah/eax: (addr handle function) <- address callee-h
- 55         find-function defs, curr-stream, callee-ah
- 56         var callee/eax: (addr function) <- lookup *callee-ah
- 57         compare callee, 0
- 58         break-if-=
- 59         perform-call callee, out, defs
- 60         break $evaluate:process-word
- 61       }
- 62       # if it's a name, push its value
- 63       {
- 64         compare bindings, 0
- 65         break-if-=
- 66         var tmp: (handle array byte)
- 67         var curr-string-ah/edx: (addr handle array byte) <- address tmp
- 68         stream-to-string curr-stream, curr-string-ah  # unfortunate leak
- 69         var curr-string/eax: (addr array byte) <- lookup *curr-string-ah
- 70         var val-storage: (handle value)
- 71         var val-ah/edi: (addr handle value) <- address val-storage
- 72         lookup-binding bindings, curr-string, val-ah
- 73         var val/eax: (addr value) <- lookup *val-ah
- 74         compare val, 0
- 75         break-if-=
- 76 #?         print-string-to-real-screen "value of "
- 77 #?         print-string-to-real-screen curr-string
- 78 #?         print-string-to-real-screen " is "
- 79 #?         print-int32-hex-to-real-screen result
- 80 #?         print-string-to-real-screen "\n"
- 81         push-value-stack out, val
- 82         break $evaluate:process-word
- 83       }
- 84       # otherwise assume it's a literal int and push it
- 85       {
- 86         var n/eax: int <- parse-decimal-int-from-stream curr-stream
- 87         push-int-to-value-stack out, n
- 88       }
- 89     }
- 90     # termination check
- 91     compare curr, end
- 92     break-if-=
- 93     # update
- 94     var next-word-ah/edx: (addr handle word) <- get curr, next
- 95     curr <- lookup *next-word-ah
- 96     #
- 97     loop
- 98   }
- 99 }
-100 
-101 fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) {
-102   var curr/esi: (addr handle function) <- copy first
-103   $find-function:loop: {
-104     var _f/eax: (addr function) <- lookup *curr
-105     var f/ecx: (addr function) <- copy _f
-106     compare f, 0
-107     break-if-=
-108     var curr-name-ah/eax: (addr handle array byte) <- get f, name
-109     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
-110     var done?/eax: boolean <- stream-data-equal? name, curr-name
-111     compare done?, 0  # false
-112     {
-113       break-if-=
-114       copy-handle *curr, out
-115       break $find-function:loop
-116     }
-117     curr <- get f, next
-118     loop
-119   }
-120 }
-121 
-122 fn perform-call _callee: (addr function), caller-stack: (addr value-stack), defs: (addr handle function) {
-123   var callee/ecx: (addr function) <- copy _callee
-124   # create bindings for args
-125   var table-storage: table
-126   var table/esi: (addr table) <- address table-storage
-127   initialize-table table, 0x10
-128   bind-args callee, caller-stack, table
-129   # obtain body
-130   var body-ah/eax: (addr handle line) <- get callee, body
-131   var body/eax: (addr line) <- lookup *body-ah
-132   # perform call
-133   var stack-storage: value-stack
-134   var stack/edi: (addr value-stack) <- address stack-storage
-135   initialize-value-stack stack, 0x10
-136 #?   print-string-to-real-screen "about to enter recursive eval\n"
-137   evaluate defs, table, body, 0, stack
-138 #?   print-string-to-real-screen "exited recursive eval\n"
-139   # stitch result from stack into caller
-140   var result/eax: int <- pop-int-from-value-stack stack
-141   push-int-to-value-stack caller-stack, result
-142 }
-143 
-144 fn bind-args _callee: (addr function), caller-stack: (addr value-stack), table: (addr table) {
-145   var callee/ecx: (addr function) <- copy _callee
-146   var curr-arg-ah/eax: (addr handle word) <- get callee, args
-147   var curr-arg/eax: (addr word) <- lookup *curr-arg-ah
-148   #
-149   var curr-key-storage: (handle array byte)
-150   var curr-key/edx: (addr handle array byte) <- address curr-key-storage
-151   {
-152     compare curr-arg, 0
-153     break-if-=
-154     # create binding
-155     word-to-string curr-arg, curr-key
-156     {
-157 #?       var tmp/eax: (addr array byte) <- lookup *curr-key
-158 #?       print-string-to-real-screen "binding "
-159 #?       print-string-to-real-screen tmp
-160 #?       print-string-to-real-screen " to "
-161       var curr-val/eax: int <- pop-int-from-value-stack caller-stack
-162 #?       print-int32-decimal-to-real-screen curr-val
-163 #?       print-string-to-real-screen "\n"
-164       bind-int-in-table table, curr-key, curr-val
-165     }
-166     #
-167     var next-arg-ah/edx: (addr handle word) <- get curr-arg, next
-168     curr-arg <- lookup *next-arg-ah
-169     loop
-170   }
-171 }
-172 
-173 # Copy of 'simplify' that just tracks the maximum stack depth needed
-174 # Doesn't actually need to simulate the stack, since every word has a predictable effect.
-175 fn max-stack-depth first-word: (addr word), final-word: (addr word) -> result/edi: int {
-176   var curr-word/eax: (addr word) <- copy first-word
-177   var curr-depth/ecx: int <- copy 0
-178   result <- copy 0
-179   $max-stack-depth:loop: {
-180     $max-stack-depth:process-word: {
-181       # handle operators
-182       {
-183         var is-add?/eax: boolean <- word-equal? curr-word, "+"
-184         compare is-add?, 0
-185         break-if-=
-186         curr-depth <- decrement
-187         break $max-stack-depth:process-word
-188       }
-189       {
-190         var is-sub?/eax: boolean <- word-equal? curr-word, "-"
-191         compare is-sub?, 0
-192         break-if-=
-193         curr-depth <- decrement
-194         break $max-stack-depth:process-word
-195       }
-196       {
-197         var is-mul?/eax: boolean <- word-equal? curr-word, "*"
-198         compare is-mul?, 0
-199         break-if-=
-200         curr-depth <- decrement
-201         break $max-stack-depth:process-word
-202       }
-203       # otherwise it's an int (do we need error-checking?)
-204       curr-depth <- increment
-205       # update max depth if necessary
-206       {
-207         compare curr-depth, result
-208         break-if-<=
-209         result <- copy curr-depth
-210       }
-211     }
-212     # if curr-word == final-word break
-213     compare curr-word, final-word
-214     break-if-=
-215     # curr-word = curr-word->next
-216     var next-word-ah/edx: (addr handle word) <- get curr-word, next
-217     curr-word <- lookup *next-word-ah
-218     #
-219     loop
-220   }
-221 }
+ 53         var done?/eax: boolean <- stream-empty? curr-stream
+ 54         compare done?, 0  # false
+ 55         break-if-!=
+ 56         var new-byte/eax: byte <- read-byte curr-stream
+ 57         compare new-byte, 0x3d  # '='
+ 58         break-if-!=
+ 59         var key-h: (handle array byte)
+ 60         var key/ecx: (addr handle array byte) <- address key-h
+ 61         stream-to-string curr-stream, key
+ 62         var foo/eax: (addr array byte) <- lookup *key
+ 63         var val/eax: int <- pop-int-from-value-stack out
+ 64         bind-int-in-table bindings, key, val
+ 65         var line/eax: (addr line) <- copy scratch
+ 66         var next-line-ah/eax: (addr handle line) <- get line, next
+ 67         var next-line/eax: (addr line) <- lookup *next-line-ah
+ 68         compare next-line, 0
+ 69         break-if-= $evaluate:process-word
+ 70         evaluate functions, bindings, next-line, end, out
+ 71         break $evaluate:process-word
+ 72       }
+ 73       rewind-stream curr-stream
+ 74       # if curr-stream is a known function name, call it appropriately
+ 75       {
+ 76         var callee-h: (handle function)
+ 77         var callee-ah/eax: (addr handle function) <- address callee-h
+ 78         find-function functions, curr-stream, callee-ah
+ 79         var callee/eax: (addr function) <- lookup *callee-ah
+ 80         compare callee, 0
+ 81         break-if-=
+ 82         perform-call callee, out, functions
+ 83         break $evaluate:process-word
+ 84       }
+ 85       # if it's a name, push its value
+ 86       {
+ 87         compare bindings, 0
+ 88         break-if-=
+ 89         var tmp: (handle array byte)
+ 90         var curr-string-ah/edx: (addr handle array byte) <- address tmp
+ 91         stream-to-string curr-stream, curr-string-ah  # unfortunate leak
+ 92         var curr-string/eax: (addr array byte) <- lookup *curr-string-ah
+ 93         var val-storage: (handle value)
+ 94         var val-ah/edi: (addr handle value) <- address val-storage
+ 95         lookup-binding bindings, curr-string, val-ah
+ 96         var val/eax: (addr value) <- lookup *val-ah
+ 97         compare val, 0
+ 98         break-if-=
+ 99         push-value-stack out, val
+100         break $evaluate:process-word
+101       }
+102       # otherwise assume it's a literal int and push it
+103       {
+104         var n/eax: int <- parse-decimal-int-from-stream curr-stream
+105         push-int-to-value-stack out, n
+106       }
+107     }
+108     # termination check
+109     compare curr, end
+110     break-if-=
+111     # update
+112     var next-word-ah/edx: (addr handle word) <- get curr, next
+113     curr <- lookup *next-word-ah
+114     #
+115     loop
+116   }
+117 }
+118 
+119 fn test-evaluate {
+120   var line-storage: line
+121   var line/esi: (addr line) <- address line-storage
+122   var first-word-ah/eax: (addr handle word) <- get line-storage, data
+123   allocate-word-with first-word-ah, "3"
+124   append-word-with *first-word-ah, "=a"
+125   var next-line-ah/eax: (addr handle line) <- get line-storage, next
+126   allocate next-line-ah
+127   var next-line/eax: (addr line) <- lookup *next-line-ah
+128   var first-word-ah/eax: (addr handle word) <- get next-line, data
+129   allocate-word-with first-word-ah, "a"
+130   var functions-storage: (handle function)
+131   var functions/ecx: (addr handle function) <- address functions-storage
+132   var table-storage: table
+133   var table/ebx: (addr table) <- address table-storage
+134   initialize-table table, 0x10
+135   var stack-storage: value-stack
+136   var stack/edi: (addr value-stack) <- address stack-storage
+137   initialize-value-stack stack, 0x10
+138   evaluate functions, table, line, 0, stack
+139   var x/eax: int <- pop-int-from-value-stack stack
+140   check-ints-equal x, 3, "F - test-evaluate"
+141 }
+142 
+143 fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) {
+144   var curr/esi: (addr handle function) <- copy first
+145   $find-function:loop: {
+146     var _f/eax: (addr function) <- lookup *curr
+147     var f/ecx: (addr function) <- copy _f
+148     compare f, 0
+149     break-if-=
+150     var curr-name-ah/eax: (addr handle array byte) <- get f, name
+151     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
+152     var done?/eax: boolean <- stream-data-equal? name, curr-name
+153     compare done?, 0  # false
+154     {
+155       break-if-=
+156       copy-handle *curr, out
+157       break $find-function:loop
+158     }
+159     curr <- get f, next
+160     loop
+161   }
+162 }
+163 
+164 fn perform-call _callee: (addr function), caller-stack: (addr value-stack), functions: (addr handle function) {
+165   var callee/ecx: (addr function) <- copy _callee
+166   # create bindings for args
+167   var table-storage: table
+168   var table/esi: (addr table) <- address table-storage
+169   initialize-table table, 0x10
+170   bind-args callee, caller-stack, table
+171   # obtain body
+172   var body-ah/eax: (addr handle line) <- get callee, body
+173   var body/eax: (addr line) <- lookup *body-ah
+174   # perform call
+175   var stack-storage: value-stack
+176   var stack/edi: (addr value-stack) <- address stack-storage
+177   initialize-value-stack stack, 0x10
+178 #?   print-string-to-real-screen "about to enter recursive eval\n"
+179   evaluate functions, table, body, 0, stack
+180 #?   print-string-to-real-screen "exited recursive eval\n"
+181   # stitch result from stack into caller
+182   var result/eax: int <- pop-int-from-value-stack stack
+183   push-int-to-value-stack caller-stack, result
+184 }
+185 
+186 # pop args from the caller-stack and bind them to successive args
+187 # implies: function args are stored in reverse order
+188 fn bind-args _callee: (addr function), caller-stack: (addr value-stack), table: (addr table) {
+189   var callee/ecx: (addr function) <- copy _callee
+190   var curr-arg-ah/eax: (addr handle word) <- get callee, args
+191   var curr-arg/eax: (addr word) <- lookup *curr-arg-ah
+192   #
+193   var curr-key-storage: (handle array byte)
+194   var curr-key/edx: (addr handle array byte) <- address curr-key-storage
+195   {
+196     compare curr-arg, 0
+197     break-if-=
+198     # create binding
+199     word-to-string curr-arg, curr-key
+200     {
+201 #?       var tmp/eax: (addr array byte) <- lookup *curr-key
+202 #?       print-string-to-real-screen "binding "
+203 #?       print-string-to-real-screen tmp
+204 #?       print-string-to-real-screen " to "
+205       var curr-val/eax: int <- pop-int-from-value-stack caller-stack
+206 #?       print-int32-decimal-to-real-screen curr-val
+207 #?       print-string-to-real-screen "\n"
+208       bind-int-in-table table, curr-key, curr-val
+209     }
+210     #
+211     var next-arg-ah/edx: (addr handle word) <- get curr-arg, next
+212     curr-arg <- lookup *next-arg-ah
+213     loop
+214   }
+215 }
+216 
+217 # Copy of 'simplify' that just tracks the maximum stack depth needed
+218 # Doesn't actually need to simulate the stack, since every word has a predictable effect.
+219 fn max-stack-depth first-word: (addr word), final-word: (addr word) -> result/edi: int {
+220   var curr-word/eax: (addr word) <- copy first-word
+221   var curr-depth/ecx: int <- copy 0
+222   result <- copy 0
+223   $max-stack-depth:loop: {
+224     $max-stack-depth:process-word: {
+225       # handle operators
+226       {
+227         var is-add?/eax: boolean <- word-equal? curr-word, "+"
+228         compare is-add?, 0
+229         break-if-=
+230         curr-depth <- decrement
+231         break $max-stack-depth:process-word
+232       }
+233       {
+234         var is-sub?/eax: boolean <- word-equal? curr-word, "-"
+235         compare is-sub?, 0
+236         break-if-=
+237         curr-depth <- decrement
+238         break $max-stack-depth:process-word
+239       }
+240       {
+241         var is-mul?/eax: boolean <- word-equal? curr-word, "*"
+242         compare is-mul?, 0
+243         break-if-=
+244         curr-depth <- decrement
+245         break $max-stack-depth:process-word
+246       }
+247       # otherwise it's an int (do we need error-checking?)
+248       curr-depth <- increment
+249       # update max depth if necessary
+250       {
+251         compare curr-depth, result
+252         break-if-<=
+253         result <- copy curr-depth
+254       }
+255     }
+256     # if curr-word == final-word break
+257     compare curr-word, final-word
+258     break-if-=
+259     # curr-word = curr-word->next
+260     var next-word-ah/edx: (addr handle word) <- get curr-word, next
+261     curr-word <- lookup *next-word-ah
+262     #
+263     loop
+264   }
+265 }
 
diff --git a/html/apps/tile/value-stack.mu.html b/html/apps/tile/value-stack.mu.html index 86117bed..3afd6075 100644 --- a/html/apps/tile/value-stack.mu.html +++ b/html/apps/tile/value-stack.mu.html @@ -153,23 +153,24 @@ if ('onhashchange' in window) { 94 var data/edi: (addr array value) <- copy _data 95 var top-addr/ecx: (addr int) <- get self, top 96 var i/ebx: int <- copy 0 - 97 result <- copy 0 + 97 var out: int 98 { 99 compare i, *top-addr 100 break-if->= 101 var o/edx: (offset value) <- compute-offset data, i 102 var g/edx: (addr value) <- index data, o 103 var g2/edx: (addr int) <- get g, scalar-data -104 var w/ecx: int <- int-width-decimal *g2 -105 compare w, result +104 var w/eax: int <- decimal-size *g2 +105 compare w, out 106 { 107 break-if-<= -108 result <- copy w +108 copy-to out, w 109 } 110 i <- increment 111 loop 112 } -113 } +113 result <- copy out +114 } diff --git a/html/apps/tile/word.mu.html b/html/apps/tile/word.mu.html index 2b93eff1..64fc37c0 100644 --- a/html/apps/tile/word.mu.html +++ b/html/apps/tile/word.mu.html @@ -14,6 +14,7 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.CommentedCode { color: #8a8a8a; } .muComment { color: #005faf; } .LineNr { } .SpecialChar { color: #d70000; } @@ -107,189 +108,415 @@ if ('onhashchange' in window) { 49 50 ## real primitives 51 - 52 fn word-equal? _self: (addr word), s: (addr array byte) -> result/eax: boolean { - 53 var self/esi: (addr word) <- copy _self - 54 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data - 55 var data/eax: (addr gap-buffer) <- lookup *data-ah - 56 result <- gap-buffer-equal? data, s - 57 } - 58 - 59 fn word-length _self: (addr word) -> result/eax: int { - 60 var self/esi: (addr word) <- copy _self - 61 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data - 62 var data/eax: (addr gap-buffer) <- lookup *data-ah - 63 result <- gap-buffer-length data - 64 } - 65 - 66 fn first-word _self: (addr word) -> result/eax: (addr word) { - 67 var self/esi: (addr word) <- copy _self - 68 var out/edi: (addr word) <- copy self - 69 var prev/esi: (addr handle word) <- get self, prev - 70 { - 71 var curr/eax: (addr word) <- lookup *prev - 72 compare curr, 0 - 73 break-if-= - 74 out <- copy curr - 75 prev <- get curr, prev - 76 loop - 77 } - 78 result <- copy out - 79 } - 80 - 81 fn final-word _self: (addr word) -> result/eax: (addr word) { - 82 var self/esi: (addr word) <- copy _self - 83 var out/edi: (addr word) <- copy self - 84 var next/esi: (addr handle word) <- get self, next - 85 { - 86 var curr/eax: (addr word) <- lookup *next - 87 compare curr, 0 - 88 break-if-= - 89 out <- copy curr - 90 next <- get curr, next - 91 loop - 92 } - 93 result <- copy out - 94 } - 95 - 96 fn first-grapheme _self: (addr word) -> result/eax: grapheme { - 97 var self/esi: (addr word) <- copy _self - 98 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data - 99 var data/eax: (addr gap-buffer) <- lookup *data-ah -100 result <- first-grapheme-in-gap-buffer data -101 } -102 -103 fn add-grapheme-to-word _self: (addr word), c: grapheme { -104 var self/esi: (addr word) <- copy _self -105 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -106 var data/eax: (addr gap-buffer) <- lookup *data-ah -107 add-grapheme-at-gap data, c -108 } -109 -110 fn cursor-at-start? _self: (addr word) -> result/eax: boolean { -111 var self/esi: (addr word) <- copy _self -112 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -113 var data/eax: (addr gap-buffer) <- lookup *data-ah -114 result <- gap-at-start? data -115 } -116 -117 fn cursor-at-end? _self: (addr word) -> result/eax: boolean { -118 var self/esi: (addr word) <- copy _self -119 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -120 var data/eax: (addr gap-buffer) <- lookup *data-ah -121 result <- gap-at-end? data -122 } -123 -124 fn cursor-left _self: (addr word) { -125 var self/esi: (addr word) <- copy _self -126 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -127 var data/eax: (addr gap-buffer) <- lookup *data-ah -128 var dummy/eax: grapheme <- gap-left data -129 } -130 -131 fn cursor-right _self: (addr word) { -132 var self/esi: (addr word) <- copy _self -133 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -134 var data/eax: (addr gap-buffer) <- lookup *data-ah -135 var dummy/eax: grapheme <- gap-right data -136 } -137 -138 fn cursor-to-start _self: (addr word) { -139 var self/esi: (addr word) <- copy _self -140 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -141 var data/eax: (addr gap-buffer) <- lookup *data-ah -142 gap-to-start data -143 } -144 -145 fn cursor-to-end _self: (addr word) { -146 var self/esi: (addr word) <- copy _self -147 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -148 var data/eax: (addr gap-buffer) <- lookup *data-ah -149 gap-to-end data -150 } -151 -152 fn cursor-index _self: (addr word) -> result/eax: int { -153 var self/esi: (addr word) <- copy _self -154 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -155 var data/eax: (addr gap-buffer) <- lookup *data-ah -156 result <- gap-index data -157 } -158 -159 fn delete-before-cursor _self: (addr word) { -160 var self/esi: (addr word) <- copy _self -161 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -162 var data/eax: (addr gap-buffer) <- lookup *data-ah -163 delete-before-gap data -164 } -165 -166 fn delete-next _self: (addr word) { -167 $delete-next:body: { + 52 fn move-word-contents _src-ah: (addr handle word), _dest-ah: (addr handle word) { + 53 var dest-ah/eax: (addr handle word) <- copy _dest-ah + 54 var _dest/eax: (addr word) <- lookup *dest-ah + 55 var dest/edi: (addr word) <- copy _dest + 56 var src-ah/eax: (addr handle word) <- copy _src-ah + 57 var _src/eax: (addr word) <- lookup *src-ah + 58 var src/esi: (addr word) <- copy _src + 59 cursor-to-start src + 60 var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data + 61 var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah + 62 var src-stack/ecx: (addr grapheme-stack) <- get src-data, right + 63 { + 64 var done?/eax: boolean <- grapheme-stack-empty? src-stack + 65 compare done?, 0 # false + 66 break-if-!= + 67 var g/eax: grapheme <- pop-grapheme-stack src-stack + 68 #? print-grapheme 0, g + 69 #? print-string 0, "\n" + 70 add-grapheme-to-word dest, g + 71 loop + 72 } + 73 } + 74 + 75 fn copy-word-contents-before-cursor _src-ah: (addr handle word), _dest-ah: (addr handle word) { + 76 var dest-ah/eax: (addr handle word) <- copy _dest-ah + 77 var _dest/eax: (addr word) <- lookup *dest-ah + 78 var dest/edi: (addr word) <- copy _dest + 79 var src-ah/eax: (addr handle word) <- copy _src-ah + 80 var src/eax: (addr word) <- lookup *src-ah + 81 var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data + 82 var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah + 83 var src-stack/ecx: (addr grapheme-stack) <- get src-data, left + 84 var src-stack-data-ah/eax: (addr handle array grapheme) <- get src-stack, data + 85 var _src-stack-data/eax: (addr array grapheme) <- lookup *src-stack-data-ah + 86 var src-stack-data/edx: (addr array grapheme) <- copy _src-stack-data + 87 var top-addr/ecx: (addr int) <- get src-stack, top + 88 var i/eax: int <- copy 0 + 89 { + 90 compare i, *top-addr + 91 break-if->= + 92 var g/edx: (addr grapheme) <- index src-stack-data, i + 93 add-grapheme-to-word dest, *g + 94 i <- increment + 95 loop + 96 } + 97 } + 98 + 99 fn word-equal? _self: (addr word), s: (addr array byte) -> result/eax: boolean { +100 var self/esi: (addr word) <- copy _self +101 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +102 var data/eax: (addr gap-buffer) <- lookup *data-ah +103 result <- gap-buffer-equal? data, s +104 } +105 +106 fn word-length _self: (addr word) -> result/eax: int { +107 var self/esi: (addr word) <- copy _self +108 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +109 var data/eax: (addr gap-buffer) <- lookup *data-ah +110 result <- gap-buffer-length data +111 } +112 +113 fn first-word _in: (addr handle word), out: (addr handle word) { +114 var curr-ah/esi: (addr handle word) <- copy _in +115 var curr/eax: (addr word) <- lookup *curr-ah +116 var prev/edi: (addr handle word) <- copy 0 +117 { +118 prev <- get curr, prev +119 var curr/eax: (addr word) <- lookup *prev +120 compare curr, 0 +121 break-if-= +122 copy-object prev, curr-ah +123 loop +124 } +125 copy-object curr-ah, out +126 } +127 +128 fn final-word _in: (addr handle word), out: (addr handle word) { +129 var curr-h: (handle word) +130 var curr-ah/esi: (addr handle word) <- address curr-h +131 copy-object _in, curr-ah +132 var curr/eax: (addr word) <- copy 0 +133 var next/edi: (addr handle word) <- copy 0 +134 { +135 curr <- lookup *curr-ah +136 next <- get curr, next +137 curr <- lookup *next +138 compare curr, 0 +139 break-if-= +140 copy-object next, curr-ah +141 loop +142 } +143 copy-object curr-ah, out +144 } +145 +146 fn first-grapheme _self: (addr word) -> result/eax: grapheme { +147 var self/esi: (addr word) <- copy _self +148 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +149 var data/eax: (addr gap-buffer) <- lookup *data-ah +150 result <- first-grapheme-in-gap-buffer data +151 } +152 +153 fn add-grapheme-to-word _self: (addr word), c: grapheme { +154 var self/esi: (addr word) <- copy _self +155 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +156 var data/eax: (addr gap-buffer) <- lookup *data-ah +157 add-grapheme-at-gap data, c +158 } +159 +160 fn cursor-at-start? _self: (addr word) -> result/eax: boolean { +161 var self/esi: (addr word) <- copy _self +162 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +163 var data/eax: (addr gap-buffer) <- lookup *data-ah +164 result <- gap-at-start? data +165 } +166 +167 fn cursor-at-end? _self: (addr word) -> result/eax: boolean { 168 var self/esi: (addr word) <- copy _self -169 var next-ah/edi: (addr handle word) <- get self, next -170 var next/eax: (addr word) <- lookup *next-ah -171 compare next, 0 -172 break-if-= $delete-next:body -173 var next-next-ah/ecx: (addr handle word) <- get next, next -174 var self-ah/esi: (addr handle word) <- get next, prev -175 copy-object next-next-ah, next-ah -176 var new-next/eax: (addr word) <- lookup *next-next-ah -177 compare new-next, 0 -178 break-if-= $delete-next:body -179 var dest/eax: (addr handle word) <- get new-next, prev -180 copy-object self-ah, dest -181 } -182 } -183 -184 fn print-word screen: (addr screen), _self: (addr word) { -185 var self/esi: (addr word) <- copy _self -186 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -187 var data/eax: (addr gap-buffer) <- lookup *data-ah -188 render-gap-buffer screen, data -189 } -190 -191 # one implication of handles: append must take a handle -192 fn append-word _self-ah: (addr handle word) { -193 var self-ah/esi: (addr handle word) <- copy _self-ah -194 var _self/eax: (addr word) <- lookup *self-ah -195 var self/ebx: (addr word) <- copy _self -196 # allocate new handle -197 var new: (handle word) -198 var new-ah/ecx: (addr handle word) <- address new -199 allocate new-ah -200 var new-addr/eax: (addr word) <- lookup new -201 initialize-word new-addr -202 # new->next = self->next -203 var src/esi: (addr handle word) <- get self, next -204 var dest/edi: (addr handle word) <- get new-addr, next -205 copy-object src, dest -206 # new->next->prev = new -207 { -208 var next-addr/eax: (addr word) <- lookup *src -209 compare next-addr, 0 -210 break-if-= -211 dest <- get next-addr, prev -212 copy-object new-ah, dest -213 } -214 # new->prev = self -215 dest <- get new-addr, prev -216 copy-object _self-ah, dest -217 # self->next = new -218 dest <- get self, next -219 copy-object new-ah, dest -220 } -221 -222 fn emit-word _self: (addr word), out: (addr stream byte) { -223 var self/esi: (addr word) <- copy _self -224 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -225 var data/eax: (addr gap-buffer) <- lookup *data-ah -226 emit-gap-buffer data, out -227 } -228 -229 fn word-to-string _self: (addr word), out: (addr handle array byte) { -230 var self/esi: (addr word) <- copy _self -231 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data -232 var data/eax: (addr gap-buffer) <- lookup *data-ah -233 gap-buffer-to-string data, out -234 } +169 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +170 var data/eax: (addr gap-buffer) <- lookup *data-ah +171 result <- gap-at-end? data +172 } +173 +174 fn cursor-left _self: (addr word) { +175 var self/esi: (addr word) <- copy _self +176 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +177 var data/eax: (addr gap-buffer) <- lookup *data-ah +178 var dummy/eax: grapheme <- gap-left data +179 } +180 +181 fn cursor-right _self: (addr word) { +182 var self/esi: (addr word) <- copy _self +183 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +184 var data/eax: (addr gap-buffer) <- lookup *data-ah +185 var dummy/eax: grapheme <- gap-right data +186 } +187 +188 fn cursor-to-start _self: (addr word) { +189 var self/esi: (addr word) <- copy _self +190 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +191 var data/eax: (addr gap-buffer) <- lookup *data-ah +192 gap-to-start data +193 } +194 +195 fn cursor-to-end _self: (addr word) { +196 var self/esi: (addr word) <- copy _self +197 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +198 var data/eax: (addr gap-buffer) <- lookup *data-ah +199 gap-to-end data +200 } +201 +202 fn cursor-index _self: (addr word) -> result/eax: int { +203 var self/esi: (addr word) <- copy _self +204 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +205 var data/eax: (addr gap-buffer) <- lookup *data-ah +206 result <- gap-index data +207 } +208 +209 fn delete-before-cursor _self: (addr word) { +210 var self/esi: (addr word) <- copy _self +211 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +212 var data/eax: (addr gap-buffer) <- lookup *data-ah +213 delete-before-gap data +214 } +215 +216 fn delete-next _self: (addr word) { +217 $delete-next:body: { +218 var self/esi: (addr word) <- copy _self +219 var next-ah/edi: (addr handle word) <- get self, next +220 var next/eax: (addr word) <- lookup *next-ah +221 compare next, 0 +222 break-if-= $delete-next:body +223 var next-next-ah/ecx: (addr handle word) <- get next, next +224 var self-ah/esi: (addr handle word) <- get next, prev +225 copy-object next-next-ah, next-ah +226 var new-next/eax: (addr word) <- lookup *next-next-ah +227 compare new-next, 0 +228 break-if-= $delete-next:body +229 var dest/eax: (addr handle word) <- get new-next, prev +230 copy-object self-ah, dest +231 } +232 } +233 +234 fn print-word screen: (addr screen), _self: (addr word) { +235 var self/esi: (addr word) <- copy _self +236 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +237 var data/eax: (addr gap-buffer) <- lookup *data-ah +238 render-gap-buffer screen, data +239 } +240 +241 fn print-words screen: (addr screen), _words-ah: (addr handle word) { +242 var words-ah/eax: (addr handle word) <- copy _words-ah +243 var words-a/eax: (addr word) <- lookup *words-ah +244 compare words-a, 0 +245 break-if-= +246 # print +247 print-word screen, words-a +248 print-string screen, " " +249 # recurse +250 var next-ah/eax: (addr handle word) <- get words-a, next +251 print-words screen, next-ah +252 } +253 +254 fn print-words-in-reverse screen: (addr screen), _words-ah: (addr handle word) { +255 var words-ah/eax: (addr handle word) <- copy _words-ah +256 var words-a/eax: (addr word) <- lookup *words-ah +257 compare words-a, 0 +258 break-if-= +259 # recurse +260 var next-ah/ecx: (addr handle word) <- get words-a, next +261 print-words screen, next-ah +262 # print +263 print-word screen, words-a +264 print-string screen, " " +265 } +266 +267 # Gotcha with some word operations: ensure dest-ah isn't in the middle of some +268 # existing chain of words. There are two pointers to patch, and you'll forget +269 # to do the other one. +270 fn copy-words _src-ah: (addr handle word), _dest-ah: (addr handle word) { +271 var src-ah/eax: (addr handle word) <- copy _src-ah +272 var src-a/eax: (addr word) <- lookup *src-ah +273 compare src-a, 0 +274 break-if-= +275 # copy +276 var dest-ah/edi: (addr handle word) <- copy _dest-ah +277 copy-word src-a, dest-ah +278 # recurse +279 var rest: (handle word) +280 var rest-ah/ecx: (addr handle word) <- address rest +281 var next-src-ah/esi: (addr handle word) <- get src-a, next +282 copy-words next-src-ah, rest-ah +283 chain-words dest-ah, rest-ah +284 } +285 +286 fn copy-words-in-reverse _src-ah: (addr handle word), _dest-ah: (addr handle word) { +287 var src-ah/eax: (addr handle word) <- copy _src-ah +288 var _src-a/eax: (addr word) <- lookup *src-ah +289 var src-a/esi: (addr word) <- copy _src-a +290 compare src-a, 0 +291 break-if-= +292 # recurse +293 var next-src-ah/ecx: (addr handle word) <- get src-a, next +294 var dest-ah/edi: (addr handle word) <- copy _dest-ah +295 copy-words-in-reverse next-src-ah, dest-ah +296 # +297 copy-word-at-end src-a, dest-ah +298 } +299 +300 fn copy-word-at-end src: (addr word), _dest-ah: (addr handle word) { +301 $copy-word-at-end:body: { +302 var dest-ah/edi: (addr handle word) <- copy _dest-ah +303 # if dest is null, copy and return +304 var dest-a/eax: (addr word) <- lookup *dest-ah +305 compare dest-a, 0 +306 { +307 break-if-!= +308 copy-word src, dest-ah +309 break $copy-word-at-end:body +310 } +311 # copy current word +312 var new: (handle word) +313 var new-ah/ecx: (addr handle word) <- address new +314 copy-word src, new-ah +315 # append it at the end +316 var curr-ah/edi: (addr handle word) <- copy dest-ah +317 { +318 var curr-a/eax: (addr word) <- lookup *curr-ah # curr-a guaranteed not to be null +319 var next-ah/ecx: (addr handle word) <- get curr-a, next +320 var next-a/eax: (addr word) <- lookup *next-ah +321 compare next-a, 0 +322 break-if-= +323 curr-ah <- copy next-ah +324 loop +325 } +326 chain-words curr-ah, new-ah +327 } +328 } +329 +330 fn append-word-at-end-with _dest-ah: (addr handle word), s: (addr array byte) { +331 $append-word-at-end-with:body: { +332 var dest-ah/edi: (addr handle word) <- copy _dest-ah +333 # if dest is null, copy and return +334 var dest-a/eax: (addr word) <- lookup *dest-ah +335 compare dest-a, 0 +336 { +337 break-if-!= +338 allocate-word-with dest-ah, s +339 break $append-word-at-end-with:body +340 } +341 # otherwise append at end +342 var curr-ah/edi: (addr handle word) <- copy dest-ah +343 { +344 var curr-a/eax: (addr word) <- lookup *curr-ah # curr-a guaranteed not to be null +345 var next-ah/ecx: (addr handle word) <- get curr-a, next +346 var next-a/eax: (addr word) <- lookup *next-ah +347 compare next-a, 0 +348 break-if-= +349 curr-ah <- copy next-ah +350 loop +351 } +352 append-word-with *curr-ah, s +353 } +354 } +355 +356 fn copy-word _src-a: (addr word), _dest-ah: (addr handle word) { +357 var dest-ah/eax: (addr handle word) <- copy _dest-ah +358 allocate dest-ah +359 var _dest-a/eax: (addr word) <- lookup *dest-ah +360 var dest-a/eax: (addr word) <- copy _dest-a +361 initialize-word dest-a +362 var dest/edi: (addr handle gap-buffer) <- get dest-a, scalar-data +363 var src-a/eax: (addr word) <- copy _src-a +364 var src/eax: (addr handle gap-buffer) <- get src-a, scalar-data +365 copy-gap-buffer src, dest +366 } +367 +368 # one implication of handles: append must take a handle +369 fn append-word _self-ah: (addr handle word) { +370 var self-ah/esi: (addr handle word) <- copy _self-ah +371 var _self/eax: (addr word) <- lookup *self-ah +372 var self/ebx: (addr word) <- copy _self +373 # allocate new handle +374 var new: (handle word) +375 var new-ah/ecx: (addr handle word) <- address new +376 allocate new-ah +377 var new-addr/eax: (addr word) <- lookup new +378 initialize-word new-addr +379 # new->next = self->next +380 var src/esi: (addr handle word) <- get self, next +381 var dest/edi: (addr handle word) <- get new-addr, next +382 copy-object src, dest +383 # new->next->prev = new +384 { +385 var next-addr/eax: (addr word) <- lookup *src +386 compare next-addr, 0 +387 break-if-= +388 dest <- get next-addr, prev +389 copy-object new-ah, dest +390 } +391 # new->prev = self +392 dest <- get new-addr, prev +393 copy-object _self-ah, dest +394 # self->next = new +395 dest <- get self, next +396 copy-object new-ah, dest +397 } +398 +399 fn chain-words _self-ah: (addr handle word), _next: (addr handle word) { +400 var self-ah/esi: (addr handle word) <- copy _self-ah +401 var _self/eax: (addr word) <- lookup *self-ah +402 var self/ecx: (addr word) <- copy _self +403 var dest/edx: (addr handle word) <- get self, next +404 var next-ah/edi: (addr handle word) <- copy _next +405 copy-object next-ah, dest +406 var next/eax: (addr word) <- lookup *next-ah +407 compare next, 0 +408 break-if-= +409 dest <- get next, prev +410 copy-object self-ah, dest +411 } +412 +413 fn emit-word _self: (addr word), out: (addr stream byte) { +414 var self/esi: (addr word) <- copy _self +415 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +416 var data/eax: (addr gap-buffer) <- lookup *data-ah +417 emit-gap-buffer data, out +418 } +419 +420 fn word-to-string _self: (addr word), out: (addr handle array byte) { +421 var self/esi: (addr word) <- copy _self +422 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +423 var data/eax: (addr gap-buffer) <- lookup *data-ah +424 gap-buffer-to-string data, out +425 } +426 +427 fn word-is-decimal-integer? _self: (addr word) -> result/eax: boolean { +428 var self/eax: (addr word) <- copy _self +429 var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data +430 var data/eax: (addr gap-buffer) <- lookup *data-ah +431 result <- gap-buffer-is-decimal-integer? data +432 } +433 +434 # ABSOLUTELY GHASTLY +435 fn word-exists? _haystack-ah: (addr handle word), _needle: (addr word) -> result/ebx: boolean { +436 var needle-name-storage: (handle addr byte) +437 var needle-name-ah/eax: (addr handle array byte) <- address needle-name-storage +438 word-to-string _needle, needle-name-ah # profligate leak +439 var _needle-name/eax: (addr array byte) <- lookup *needle-name-ah +440 var needle-name/edi: (addr array byte) <- copy _needle-name +441 # base case +442 result <- copy 0 # false +443 var haystack-ah/esi: (addr handle word) <- copy _haystack-ah +444 var curr/eax: (addr word) <- lookup *haystack-ah +445 compare curr, 0 +446 break-if-= +447 # check curr +448 var curr-name-storage: (handle addr byte) +449 var curr-name-ah/ecx: (addr handle array byte) <- address curr-name-storage +450 word-to-string curr, curr-name-ah # profligate leak +451 var curr-name/eax: (addr array byte) <- lookup *curr-name-ah +452 var found?/eax: boolean <- string-equal? needle-name, curr-name +453 result <- copy found? +454 compare result, 0 +455 break-if-!= +456 # recurse +457 var curr/eax: (addr word) <- lookup *haystack-ah +458 var next-haystack-ah/eax: (addr handle word) <- get curr, next +459 result <- word-exists? next-haystack-ah, _needle +460 } -- cgit 1.4.1-2-gfad0