diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2021-03-03 22:09:50 -0800 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2021-03-03 22:21:03 -0800 |
commit | 71e4f3812982dba2efb471283d310224e8db363e (patch) | |
tree | ea111a1acb8b8845dbda39c0e1b4bac1d198143b /linux/tile | |
parent | c6b928be29ac8cdb4e4d6e1eaa20420ff03e5a4c (diff) | |
download | mu-71e4f3812982dba2efb471283d310224e8db363e.tar.gz |
7842 - new directory organization
Baremetal is now the default build target and therefore has its sources at the top-level. Baremetal programs build using the phase-2 Mu toolchain that requires a Linux kernel. This phase-2 codebase which used to be at the top-level is now under the linux/ directory. Finally, the phase-2 toolchain, while self-hosting, has a way to bootstrap from a C implementation, which is now stored in linux/bootstrap. The bootstrap C implementation uses some literate programming tools that are now in linux/bootstrap/tools. So the whole thing has gotten inverted. Each directory should build one artifact and include the main sources (along with standard library). Tools used for building it are relegated to sub-directories, even though those tools are often useful in their own right, and have had lots of interesting programs written using them. A couple of things have gotten dropped in this process: - I had old ways to run on just a Linux kernel, or with a Soso kernel. No more. - I had some old tooling for running a single test at the cursor. I haven't used that lately. Maybe I'll bring it back one day. The reorg isn't done yet. Still to do: - redo documentation everywhere. All the README files, all other markdown, particularly vocabulary.md. - clean up how-to-run comments at the start of programs everywhere - rethink what to do with the html/ directory. Do we even want to keep supporting it? In spite of these shortcomings, all the scripts at the top-level, linux/ and linux/bootstrap are working. The names of the scripts also feel reasonable. This is a good milestone to take stock at.
Diffstat (limited to 'linux/tile')
-rw-r--r-- | linux/tile/README.md | 33 | ||||
-rw-r--r-- | linux/tile/box.mu | 109 | ||||
-rw-r--r-- | linux/tile/data.mu | 641 | ||||
-rw-r--r-- | linux/tile/environment.mu | 2181 | ||||
-rw-r--r-- | linux/tile/float-stack.mu | 64 | ||||
-rw-r--r-- | linux/tile/gap-buffer.mu | 343 | ||||
-rw-r--r-- | linux/tile/grapheme-stack.mu | 191 | ||||
-rw-r--r-- | linux/tile/main.mu | 133 | ||||
-rw-r--r-- | linux/tile/rpn.mu | 911 | ||||
-rw-r--r-- | linux/tile/surface.mu | 412 | ||||
-rw-r--r-- | linux/tile/table.mu | 165 | ||||
-rw-r--r-- | linux/tile/value-stack.mu | 149 | ||||
-rw-r--r-- | linux/tile/value.mu | 424 | ||||
-rw-r--r-- | linux/tile/vimrc.vim | 2 | ||||
-rw-r--r-- | linux/tile/word.mu | 573 |
15 files changed, 6331 insertions, 0 deletions
diff --git a/linux/tile/README.md b/linux/tile/README.md new file mode 100644 index 00000000..a13f7662 --- /dev/null +++ b/linux/tile/README.md @@ -0,0 +1,33 @@ +A programming environment that tries to [“stop drawing dead fish”](http://worrydream.com/#!/StopDrawingDeadFish). + +<img alt='screenshot' src='../../html/rpn5.png' width='500px'> + +To run: + +``` +./translate_mu apps/tile/*.mu +./a.elf screen +``` + +To run tests: + +``` +./a.elf test +``` + +To run a conventional REPL (for debugging): + +``` +./a.elf type +``` + +## hacking + +Unlike the top-level directory, this is just a prototype so far. There are no +tests. + +To add a new primitive you'll need to hard-code it into the `evaluate` +function (apps/tile/rpn.mu). + +There's also a second place you'll want to teach about predefined primitives: +`bound-function?` (apps/tile/environment.mu) diff --git a/linux/tile/box.mu b/linux/tile/box.mu new file mode 100644 index 00000000..859d0b8e --- /dev/null +++ b/linux/tile/box.mu @@ -0,0 +1,109 @@ +fn draw-box screen: (addr screen), row1: int, col1: int, row2: int, col2: int { + draw-horizontal-line screen, row1, col1, col2 + draw-vertical-line screen, row1, row2, col1 + draw-horizontal-line screen, row2, col1, col2 + draw-vertical-line screen, row1, row2, col2 + draw-top-left-corner screen, row1, col1 + draw-top-right-corner screen, row1, col2 + draw-bottom-left-corner screen, row2, col1 + draw-bottom-right-corner screen, row2, col2 +} + +fn draw-hatching screen: (addr screen), row1: int, col1: int, row2: int, col2: int { + var c/eax: int <- copy col1 + var r1/ecx: int <- copy row1 + r1 <- increment + c <- add 2 + { + compare c, col2 + break-if->= + draw-vertical-line screen, r1, row2, c + c <- add 2 + loop + } +} + +fn draw-horizontal-line screen: (addr screen), row: int, col1: int, col2: int { + var col/eax: int <- copy col1 + move-cursor screen, row, col + { + compare col, col2 + break-if->= + print-code-point screen, 0x2500 + col <- increment + loop + } +} + +fn draw-vertical-line screen: (addr screen), row1: int, row2: int, col: int { + var row/eax: int <- copy row1 + { + compare row, row2 + break-if->= + move-cursor screen, row, col + print-code-point screen, 0x2502 + row <- increment + loop + } +} + +fn draw-top-left-corner screen: (addr screen), row: int, col: int { + move-cursor screen, row, col + print-code-point screen, 0x250c +} + +fn draw-top-right-corner screen: (addr screen), row: int, col: int { + move-cursor screen, row, col + print-code-point screen, 0x2510 +} + +fn draw-bottom-left-corner screen: (addr screen), row: int, col: int { + move-cursor screen, row, col + print-code-point screen, 0x2514 +} + +fn draw-bottom-right-corner screen: (addr screen), row: int, col: int { + move-cursor screen, row, col + print-code-point screen, 0x2518 +} + +# erase parts of screen the slow way +fn clear-rect screen: (addr screen), row1: int, col1: int, row2: int, col2: int { + var i/eax: int <- copy row1 + { + compare i, row2 + break-if-> + var j/ecx: int <- copy col1 + move-cursor screen, i, j + { + compare j, col2 + break-if-> + print-grapheme screen 0x20/space + j <- increment + loop + } + i <- increment + loop + } +} + +fn clear-rect2 screen: (addr screen), row1: int, col1: int, w: int, h: int { + var i/eax: int <- copy 0 + var curr-row/esi: int <- copy row1 + { + compare i, w + break-if->= + move-cursor screen, curr-row, col1 + var j/ecx: int <- copy 0 + { + compare j, h + break-if->= + print-grapheme screen 0x20/space + j <- increment + loop + } + i <- increment + curr-row <- increment + loop + } +} diff --git a/linux/tile/data.mu b/linux/tile/data.mu new file mode 100644 index 00000000..d711e7d7 --- /dev/null +++ b/linux/tile/data.mu @@ -0,0 +1,641 @@ +# widgets in the environment share the following pattern of updates: +# process-* functions read keys and update which object the cursor is at +# render-* functions print to screen and update which row/col each object's cursor is at + +type sandbox { + setup: (handle line) + data: (handle line) + # bookkeeping for process-* + cursor-call-path: (handle call-path-element) + expanded-words: (handle call-path) + partial-name-for-cursor-word: (handle word) # only when renaming word + partial-name-for-function: (handle word) # only when defining function + # bookkeeping for render-* + cursor-row: int + cursor-col: int + # + next: (handle sandbox) + prev: (handle sandbox) +} + +type function { + name: (handle array byte) + args: (handle word) # in reverse order + body: (handle line) + # bookkeeping for process-* + cursor-word: (handle word) + # bookkeeping for render-* + cursor-row: int + cursor-col: int + # todo: some sort of indication of spatial location + next: (handle function) +} + +type line { + name: (handle array byte) + data: (handle word) + result: (handle result) # might be cached + next: (handle line) + prev: (handle line) +} + +type word { + scalar-data: (handle gap-buffer) + next: (handle word) + prev: (handle word) +} + +# todo: turn this into a sum type +type value { + type: int + number-data: float # if type = 0 + text-data: (handle array byte) # if type = 1 + array-data: (handle array value) # if type = 2 + file-data: (handle buffered-file) # if type = 3 + filename: (handle array byte) # if type = 3 + screen-data: (handle screen) # if type = 4 +} + +type table { + data: (handle array bind) + next: (handle table) +} + +type bind { + key: (handle array byte) + value: (handle value) # I'd inline this but we sometimes want to return a specific value from a table +} + +# A call-path is a data structure that can unambiguously refer to any specific +# call arbitrarily deep inside the call hierarchy of a program. +type call-path { + data: (handle call-path-element) + next: (handle call-path) +} + +# A call-path element is a list of elements, each of which corresponds to some call. +type call-path-element { + word: (handle word) + next: (handle call-path-element) +} + +type result { + data: value-stack + error: (handle array byte) # single error message for now +} + +fn initialize-sandbox _sandbox: (addr sandbox) { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var line-ah/eax: (addr handle line) <- get sandbox, data + allocate line-ah + var line/eax: (addr line) <- lookup *line-ah + initialize-line line + var word-ah/ecx: (addr handle word) <- get line, data + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + allocate cursor-call-path-ah + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var dest/eax: (addr handle word) <- get cursor-call-path, word + copy-object word-ah, dest +} + +# initialize line with a single empty word +fn initialize-line _line: (addr line) { + var line/esi: (addr line) <- copy _line + var word-ah/eax: (addr handle word) <- get line, data + allocate word-ah + var word/eax: (addr word) <- lookup *word-ah + initialize-word word +} + +fn create-primitive-functions _self: (addr handle function) { + # x 2* = x 2 * + var self/esi: (addr handle function) <- copy _self + allocate self + var _f/eax: (addr function) <- lookup *self + var f/esi: (addr function) <- copy _f + var name-ah/eax: (addr handle array byte) <- get f, name + populate-text-with name-ah, "2*" + var args-ah/eax: (addr handle word) <- get f, args + allocate args-ah + var args/eax: (addr word) <- lookup *args-ah + initialize-word-with args, "x" + var body-ah/eax: (addr handle line) <- get f, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + initialize-line body + var curr-word-ah/ecx: (addr handle word) <- get body, data + parse-words "x 2 *", curr-word-ah + var cursor-word-ah/edx: (addr handle word) <- get f, cursor-word + copy-object curr-word-ah, cursor-word-ah + # x 1+ = x 1 + + var next/esi: (addr handle function) <- get f, next + allocate next + var _f/eax: (addr function) <- lookup *next + var f/esi: (addr function) <- copy _f + var name-ah/eax: (addr handle array byte) <- get f, name + populate-text-with name-ah, "1+" + var args-ah/eax: (addr handle word) <- get f, args + allocate args-ah + var args/eax: (addr word) <- lookup *args-ah + initialize-word-with args, "x" + var body-ah/eax: (addr handle line) <- get f, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + initialize-line body + curr-word-ah <- get body, data + parse-words "x 1 +", curr-word-ah + var cursor-word-ah/edx: (addr handle word) <- get f, cursor-word + copy-object curr-word-ah, cursor-word-ah + # x 2+ = x 1+ 1+ + var next/esi: (addr handle function) <- get f, next + allocate next + var _f/eax: (addr function) <- lookup *next + var f/esi: (addr function) <- copy _f + var name-ah/eax: (addr handle array byte) <- get f, name + populate-text-with name-ah, "2+" + var args-ah/eax: (addr handle word) <- get f, args + allocate args-ah + var args/eax: (addr word) <- lookup *args-ah + initialize-word-with args, "x" + var body-ah/eax: (addr handle line) <- get f, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + initialize-line body + curr-word-ah <- get body, data + parse-words "x 1+ 1+", curr-word-ah + var cursor-word-ah/edx: (addr handle word) <- get f, cursor-word + copy-object curr-word-ah, cursor-word-ah + # x square = x x * + var next/esi: (addr handle function) <- get f, next + allocate next + var _f/eax: (addr function) <- lookup *next + var f/esi: (addr function) <- copy _f + var name-ah/eax: (addr handle array byte) <- get f, name + populate-text-with name-ah, "square" + var args-ah/eax: (addr handle word) <- get f, args + allocate args-ah + var args/eax: (addr word) <- lookup *args-ah + initialize-word-with args, "x" + var body-ah/eax: (addr handle line) <- get f, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + initialize-line body + curr-word-ah <- get body, data + parse-words "x x *", curr-word-ah + var cursor-word-ah/edx: (addr handle word) <- get f, cursor-word + copy-object curr-word-ah, cursor-word-ah + # x 1- = x 1 - + var next/esi: (addr handle function) <- get f, next + allocate next + var _f/eax: (addr function) <- lookup *next + var f/esi: (addr function) <- copy _f + var name-ah/eax: (addr handle array byte) <- get f, name + populate-text-with name-ah, "1-" + var args-ah/eax: (addr handle word) <- get f, args + allocate args-ah + var args/eax: (addr word) <- lookup *args-ah + initialize-word-with args, "x" + var body-ah/eax: (addr handle line) <- get f, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + initialize-line body + curr-word-ah <- get body, data + parse-words "x 1 -", curr-word-ah + var cursor-word-ah/edx: (addr handle word) <- get f, cursor-word + copy-object curr-word-ah, cursor-word-ah + # x y sub = x y - + var next/esi: (addr handle function) <- get f, next + allocate next + var _f/eax: (addr function) <- lookup *next + var f/esi: (addr function) <- copy _f + var name-ah/eax: (addr handle array byte) <- get f, name + populate-text-with name-ah, "sub" + # critical lesson: args are stored in reverse order + var args-ah/eax: (addr handle word) <- get f, args + allocate args-ah + var args/eax: (addr word) <- lookup *args-ah + initialize-word-with args, "y" + var next-arg-ah/eax: (addr handle word) <- get args, next + allocate next-arg-ah + var next-arg/eax: (addr word) <- lookup *next-arg-ah + initialize-word-with next-arg, "x" + var body-ah/eax: (addr handle line) <- get f, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + initialize-line body + curr-word-ah <- get body, data + parse-words "x y -", curr-word-ah + var cursor-word-ah/edx: (addr handle word) <- get f, cursor-word + copy-object curr-word-ah, cursor-word-ah +} + +fn function-body functions: (addr handle function), _word: (addr handle word), out: (addr handle line) { + var function-name-storage: (handle array byte) + var function-name-ah/ecx: (addr handle array byte) <- address function-name-storage + var word-ah/esi: (addr handle word) <- copy _word + var word/eax: (addr word) <- lookup *word-ah + var gap-ah/eax: (addr handle gap-buffer) <- get word, scalar-data + var gap/eax: (addr gap-buffer) <- lookup *gap-ah + gap-buffer-to-string gap, function-name-ah + var _function-name/eax: (addr array byte) <- lookup *function-name-ah + var function-name/esi: (addr array byte) <- copy _function-name + var curr-ah/ecx: (addr handle function) <- copy functions + $function-body:loop: { + var _curr/eax: (addr function) <- lookup *curr-ah + var curr/edx: (addr function) <- copy _curr + compare curr, 0 + break-if-= + var curr-name-ah/eax: (addr handle array byte) <- get curr, name + var curr-name/eax: (addr array byte) <- lookup *curr-name-ah + var found?/eax: boolean <- string-equal? curr-name, function-name + compare found?, 0/false + { + break-if-= + var src/eax: (addr handle line) <- get curr, body + copy-object src, out + break $function-body:loop + } + curr-ah <- get curr, next + loop + } +} + +fn body-length functions: (addr handle function), function-name: (addr handle word) -> _/eax: int { + var body-storage: (handle line) + var body-ah/edi: (addr handle line) <- address body-storage + function-body functions, function-name, body-ah + var body/eax: (addr line) <- lookup *body-ah + var result/eax: int <- num-words-in-line body + return result +} + +fn num-words-in-line _in: (addr line) -> _/eax: int { + var in/esi: (addr line) <- copy _in + var curr-ah/ecx: (addr handle word) <- get in, data + var result/edi: int <- copy 0 + { + var curr/eax: (addr word) <- lookup *curr-ah + compare curr, 0 + break-if-= + curr-ah <- get curr, next + result <- increment + loop + } + return result +} + +fn populate-text-with _out: (addr handle array byte), _in: (addr array byte) { + var in/esi: (addr array byte) <- copy _in + var n/ecx: int <- length in + var out/edx: (addr handle array byte) <- copy _out + populate out, n + var _out-addr/eax: (addr array byte) <- lookup *out + var out-addr/edx: (addr array byte) <- copy _out-addr + var i/eax: int <- copy 0 + { + compare i, n + break-if->= + var src/esi: (addr byte) <- index in, i + var val/ecx: byte <- copy-byte *src + var dest/edi: (addr byte) <- index out-addr, i + copy-byte-to *dest, val + i <- increment + loop + } +} + +fn initialize-path-from-sandbox _in: (addr sandbox), _out: (addr handle call-path-element) { + var sandbox/esi: (addr sandbox) <- copy _in + var line-ah/eax: (addr handle line) <- get sandbox, data + var line/eax: (addr line) <- lookup *line-ah + var src/esi: (addr handle word) <- get line, data + var out-ah/edi: (addr handle call-path-element) <- copy _out + var out/eax: (addr call-path-element) <- lookup *out-ah + var dest/edi: (addr handle word) <- get out, word + copy-object src, dest +} + +fn initialize-path-from-line _line: (addr line), _out: (addr handle call-path-element) { + var line/eax: (addr line) <- copy _line + var src/esi: (addr handle word) <- get line, data + var out-ah/edi: (addr handle call-path-element) <- copy _out + var out/eax: (addr call-path-element) <- lookup *out-ah + var dest/edi: (addr handle word) <- get out, word + copy-object src, dest +} + +fn find-in-call-paths call-paths: (addr handle call-path), needle: (addr handle call-path-element) -> _/eax: boolean { + var curr-ah/esi: (addr handle call-path) <- copy call-paths + $find-in-call-path:loop: { + var curr/eax: (addr call-path) <- lookup *curr-ah + compare curr, 0 + break-if-= + { + var curr-data/eax: (addr handle call-path-element) <- get curr, data + var match?/eax: boolean <- call-path-element-match? curr-data, needle + compare match?, 0/false + { + break-if-= + return 1/true + } + } + curr-ah <- get curr, next + loop + } + return 0/false +} + +fn call-path-element-match? _x: (addr handle call-path-element), _y: (addr handle call-path-element) -> _/eax: boolean { + var x-ah/eax: (addr handle call-path-element) <- copy _x + var x-a/eax: (addr call-path-element) <- lookup *x-ah + var x/esi: (addr call-path-element) <- copy x-a + var y-ah/eax: (addr handle call-path-element) <- copy _y + var y-a/eax: (addr call-path-element) <- lookup *y-ah + var y/edi: (addr call-path-element) <- copy y-a + compare x, y + { + break-if-!= + return 1/true + } + compare x, 0 + { + break-if-!= + return 0/false + } + compare y, 0 + { + break-if-!= + return 0/false + } + # compare word addresses, not contents + var x-data-ah/ecx: (addr handle word) <- get x, word + var x-data-a/eax: (addr word) <- lookup *x-data-ah + var x-data/ecx: int <- copy x-data-a + var y-data-ah/eax: (addr handle word) <- get y, word + var y-data-a/eax: (addr word) <- lookup *y-data-ah + var y-data/eax: int <- copy y-data-a +#? print-string 0, "match? " +#? print-int32-hex 0, x-data +#? print-string 0, " vs " +#? print-int32-hex 0, y-data +#? print-string 0, "\n" + compare x-data, y-data + { + break-if-= + return 0/false + } + var x-next/ecx: (addr handle call-path-element) <- get x, next + var y-next/eax: (addr handle call-path-element) <- get y, next + var result/eax: boolean <- call-path-element-match? x-next, y-next + return result +} + +# order is irrelevant +fn insert-in-call-path list: (addr handle call-path), new: (addr handle call-path-element) { + var new-path-storage: (handle call-path) + var new-path-ah/edi: (addr handle call-path) <- address new-path-storage + allocate new-path-ah + var new-path/eax: (addr call-path) <- lookup *new-path-ah + var next/ecx: (addr handle call-path) <- get new-path, next + copy-object list, next + var dest/ecx: (addr handle call-path-element) <- get new-path, data + deep-copy-call-path-element new, dest + copy-object new-path-ah, list +} + +# assumes dest is initially clear +fn deep-copy-call-path-element _src: (addr handle call-path-element), _dest: (addr handle call-path-element) { + var src/esi: (addr handle call-path-element) <- copy _src + # if src is null, return + var _src-addr/eax: (addr call-path-element) <- lookup *src + compare _src-addr, 0 + break-if-= + # allocate + var src-addr/esi: (addr call-path-element) <- copy _src-addr + var dest/eax: (addr handle call-path-element) <- copy _dest + allocate dest + # copy data + var dest-addr/eax: (addr call-path-element) <- lookup *dest + { + var dest-data-addr/ecx: (addr handle word) <- get dest-addr, word + var src-data-addr/eax: (addr handle word) <- get src-addr, word + copy-object src-data-addr, dest-data-addr + } + # recurse + var src-next/esi: (addr handle call-path-element) <- get src-addr, next + var dest-next/eax: (addr handle call-path-element) <- get dest-addr, next + deep-copy-call-path-element src-next, dest-next +} + +fn delete-in-call-path list: (addr handle call-path), needle: (addr handle call-path-element) { + var curr-ah/esi: (addr handle call-path) <- copy list + $delete-in-call-path:loop: { + var _curr/eax: (addr call-path) <- lookup *curr-ah + var curr/ecx: (addr call-path) <- copy _curr + compare curr, 0 + break-if-= + { + var curr-data/eax: (addr handle call-path-element) <- get curr, data + var match?/eax: boolean <- call-path-element-match? curr-data, needle + compare match?, 0/false + { + break-if-= + var next-ah/ecx: (addr handle call-path) <- get curr, next + copy-object next-ah, curr-ah + loop $delete-in-call-path:loop + } + } + curr-ah <- get curr, next + loop + } +} + +fn increment-final-element list: (addr handle call-path-element) { + var final-ah/eax: (addr handle call-path-element) <- copy list + var final/eax: (addr call-path-element) <- lookup *final-ah + var val-ah/ecx: (addr handle word) <- get final, word + var val/eax: (addr word) <- lookup *val-ah + var new-ah/edx: (addr handle word) <- get val, next + var target/eax: (addr word) <- lookup *new-ah + compare target, 0 + break-if-= + copy-object new-ah, val-ah +} + +fn decrement-final-element list: (addr handle call-path-element) { + var final-ah/eax: (addr handle call-path-element) <- copy list + var final/eax: (addr call-path-element) <- lookup *final-ah + var val-ah/ecx: (addr handle word) <- get final, word + var val/eax: (addr word) <- lookup *val-ah +#? print-string 0, "replacing " +#? { +#? var foo/eax: int <- copy val +#? print-int32-hex 0, foo +#? } + var new-ah/edx: (addr handle word) <- get val, prev + var target/eax: (addr word) <- lookup *new-ah + compare target, 0 + break-if-= + # val = val->prev +#? print-string 0, " with " +#? { +#? var foo/eax: int <- copy target +#? print-int32-hex 0, foo +#? } +#? print-string 0, "\n" + copy-object new-ah, val-ah +} + +fn move-final-element-to-start-of-line list: (addr handle call-path-element) { + var final-ah/eax: (addr handle call-path-element) <- copy list + var final/eax: (addr call-path-element) <- lookup *final-ah + var val-ah/ecx: (addr handle word) <- get final, word + var val/eax: (addr word) <- lookup *val-ah + var new-ah/edx: (addr handle word) <- get val, prev + var target/eax: (addr word) <- lookup *new-ah + compare target, 0 + break-if-= + copy-object new-ah, val-ah + move-final-element-to-start-of-line list +} + +fn move-final-element-to-end-of-line list: (addr handle call-path-element) { + var final-ah/eax: (addr handle call-path-element) <- copy list + var final/eax: (addr call-path-element) <- lookup *final-ah + var val-ah/ecx: (addr handle word) <- get final, word + var val/eax: (addr word) <- lookup *val-ah + var new-ah/edx: (addr handle word) <- get val, next + var target/eax: (addr word) <- lookup *new-ah + compare target, 0 + break-if-= + copy-object new-ah, val-ah + move-final-element-to-end-of-line list +} + +fn push-to-call-path-element list: (addr handle call-path-element), new: (addr handle word) { + var new-element-storage: (handle call-path-element) + var new-element-ah/edi: (addr handle call-path-element) <- address new-element-storage + allocate new-element-ah + var new-element/eax: (addr call-path-element) <- lookup *new-element-ah + # save word + var dest/ecx: (addr handle word) <- get new-element, word + copy-object new, dest + # save next + var dest2/ecx: (addr handle call-path-element) <- get new-element, next + copy-object list, dest2 + # return + copy-object new-element-ah, list +} + +fn drop-from-call-path-element _list: (addr handle call-path-element) { + var list-ah/esi: (addr handle call-path-element) <- copy _list + var list/eax: (addr call-path-element) <- lookup *list-ah + var next/eax: (addr handle call-path-element) <- get list, next + copy-object next, _list +} + +fn drop-nested-calls _list: (addr handle call-path-element) { + var list-ah/esi: (addr handle call-path-element) <- copy _list + var list/eax: (addr call-path-element) <- lookup *list-ah + var next-ah/edi: (addr handle call-path-element) <- get list, next + var next/eax: (addr call-path-element) <- lookup *next-ah + compare next, 0 + break-if-= + copy-object next-ah, _list + drop-nested-calls _list +} + +fn dump-call-path-element screen: (addr screen), _x-ah: (addr handle call-path-element) { + var x-ah/ecx: (addr handle call-path-element) <- copy _x-ah + var _x/eax: (addr call-path-element) <- lookup *x-ah + var x/esi: (addr call-path-element) <- copy _x + var word-ah/eax: (addr handle word) <- get x, word + var word/eax: (addr word) <- lookup *word-ah + print-word screen, word + var next-ah/ecx: (addr handle call-path-element) <- get x, next + var next/eax: (addr call-path-element) <- lookup *next-ah + compare next, 0 + { + break-if-= + print-string screen, " " + dump-call-path-element screen, next-ah + return + } + print-string screen, "\n" +} + +fn dump-call-paths screen: (addr screen), _x-ah: (addr handle call-path) { + var x-ah/ecx: (addr handle call-path) <- copy _x-ah + var x/eax: (addr call-path) <- lookup *x-ah + compare x, 0 + break-if-= + var src/ecx: (addr handle call-path-element) <- get x, data + dump-call-path-element screen, src + var next-ah/ecx: (addr handle call-path) <- get x, next + var next/eax: (addr call-path) <- lookup *next-ah + compare next, 0 + { + break-if-= + dump-call-paths screen, next-ah + } +} + +fn function-width _self: (addr function) -> _/eax: int { + var self/esi: (addr function) <- copy _self + var args/ecx: (addr handle word) <- get self, args + var arg-width/eax: int <- word-list-length args + var result/edi: int <- copy arg-width + result <- add 4 # function-header-indent + body-indent + var body-ah/eax: (addr handle line) <- get self, body + var body-width/eax: int <- body-width body-ah + body-width <- add 1 # right margin + body-width <- add 2 # body-indent for "≡ " + compare result, body-width + { + break-if->= + result <- copy body-width + } + return result +} + +fn body-width lines: (addr handle line) -> _/eax: int { + var curr-ah/esi: (addr handle line) <- copy lines + var result/edi: int <- copy 0 + { + var curr/eax: (addr line) <- lookup *curr-ah + compare curr, 0 + break-if-= + { + var words/ecx: (addr handle word) <- get curr, data + var curr-len/eax: int <- word-list-length words + compare curr-len, result + break-if-<= + result <- copy curr-len + } + curr-ah <- get curr, next + loop + } + return result +} + +fn function-height _self: (addr function) -> _/eax: int { + var self/esi: (addr function) <- copy _self + var body-ah/eax: (addr handle line) <- get self, body + var result/eax: int <- line-list-length body-ah + result <- increment # for function header + return result +} + +fn line-list-length lines: (addr handle line) -> _/eax: int { + var curr-ah/esi: (addr handle line) <- copy lines + var result/edi: int <- copy 0 + { + var curr/eax: (addr line) <- lookup *curr-ah + compare curr, 0 + break-if-= + curr-ah <- get curr, next + result <- increment + loop + } + return result +} diff --git a/linux/tile/environment.mu b/linux/tile/environment.mu new file mode 100644 index 00000000..de771dee --- /dev/null +++ b/linux/tile/environment.mu @@ -0,0 +1,2181 @@ +# The architecture that seems to be crystallizing: the environment has two +# areas: functions and sandbox. +# +# Rendering the environment requires rendering all areas. +# Displaying the cursor requires displaying cursor for the area controlling the cursor. +# Processing events for the environment requires processing events for the area controlling the cursor. +# +# Areas can have dialogs. +# There can also be global dialogs (currently just one: goto function). +# Areas are responsible for rendering their dialogs. +# In practice this results in dialogs encapsulating the state they need to +# decide whether to render. +# +# This will be useful if we add more areas in the future. + +type environment { + screen: (handle screen) + functions: (handle function) + sandboxes: (handle sandbox) + partial-function-name: (handle word) + # at most one of these will be set + cursor-function: (handle function) + cursor-sandbox: (handle sandbox) + # + nrows: int + ncols: int + code-separator-col: int +} + +fn initialize-environment _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + # initialize some predefined function definitions + var functions/eax: (addr handle function) <- get env, functions + create-primitive-functions functions + # initialize first sandbox + var sandbox-ah/ecx: (addr handle sandbox) <- get env, sandboxes + allocate sandbox-ah + var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah + initialize-sandbox sandbox + # initialize cursor sandbox + var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox + copy-object sandbox-ah, cursor-sandbox-ah + # initialize screen + var screen-ah/eax: (addr handle screen) <- get env, screen + var _screen/eax: (addr screen) <- lookup *screen-ah + var screen/edi: (addr screen) <- copy _screen + var nrows/eax: int <- copy 0 + var ncols/ecx: int <- copy 0 + nrows, ncols <- screen-size screen + var dest/edx: (addr int) <- get env, nrows + copy-to *dest, nrows + dest <- get env, ncols + copy-to *dest, ncols + var repl-col/ecx: int <- copy ncols + repl-col <- shift-right 1 + dest <- get env, code-separator-col + copy-to *dest, repl-col +} + +fn initialize-environment-with-fake-screen _self: (addr environment), nrows: int, ncols: int { + var self/esi: (addr environment) <- copy _self + var screen-ah/eax: (addr handle screen) <- get self, screen + allocate screen-ah + var screen-addr/eax: (addr screen) <- lookup *screen-ah + initialize-screen screen-addr, nrows, ncols + initialize-environment self +} + +############# +# Iterate +############# + +fn process _self: (addr environment), key: grapheme { + var self/esi: (addr environment) <- copy _self + var fn-name-ah/eax: (addr handle word) <- get self, partial-function-name + var fn-name/eax: (addr word) <- lookup *fn-name-ah + compare fn-name, 0 + { + break-if-= +#? print-string 0, "processing goto fn\n" + process-goto-dialog self, key + return + } + var function-ah/eax: (addr handle function) <- get self, cursor-function + var function/eax: (addr function) <- lookup *function-ah + compare function, 0 + { + break-if-= +#? print-string 0, "processing function\n" + process-function self, function, key + return + } + var sandbox-ah/eax: (addr handle sandbox) <- get self, cursor-sandbox + var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah + compare sandbox, 0 + { + break-if-= +#? print-string 0, "processing sandbox\n" + process-sandbox self, sandbox, key + return + } +} + +# collect new name in partial-function-name, and move the cursor to function with that name +fn process-goto-dialog _self: (addr environment), key: grapheme { + var self/esi: (addr environment) <- copy _self + var fn-name-ah/edi: (addr handle word) <- get self, partial-function-name + # if 'esc' pressed, cancel goto + compare key, 0x1b/esc + $process-goto-dialog:cancel: { + break-if-!= + clear-object fn-name-ah + return + } + # if 'enter' pressed, location function and set cursor to it + compare key, 0xa/enter + $process-goto-dialog:commit: { + break-if-!= +#? print-string 0, "jump\n" + var fn-name/eax: (addr word) <- lookup *fn-name-ah + var functions/ecx: (addr handle function) <- get self, functions + var dest/edx: (addr handle function) <- get self, cursor-function + callee functions, fn-name, dest + # we won't clear cursor-sandbox until we start supporting multiple sandboxes + clear-object fn-name-ah + # there shouldn't be any need to clear state for other dialogs in the sandbox + return + } + # + compare key, 0x7f/del # backspace on Macs + $process-goto-dialog:backspace: { + break-if-!= + # if not at start, delete grapheme before cursor + var fn-name/eax: (addr word) <- lookup *fn-name-ah + var at-start?/eax: boolean <- cursor-at-start? fn-name + compare at-start?, 0/false + { + break-if-!= + var fn-name/eax: (addr word) <- lookup *fn-name-ah + delete-before-cursor fn-name + } + return + } + # otherwise insert key within current word + var print?/eax: boolean <- real-grapheme? key + $process-goto-dialog:real-grapheme: { + compare print?, 0/false + break-if-= + var fn-name/eax: (addr word) <- lookup *fn-name-ah + add-grapheme-to-word fn-name, key + return + } + # silently ignore other hotkeys +} + +fn process-function _self: (addr environment), _function: (addr function), key: grapheme { + var self/esi: (addr environment) <- copy _self + var function/edi: (addr function) <- copy _function + process-function-edit self, function, key +} + +fn process-function-edit _self: (addr environment), _function: (addr function), key: grapheme { + var self/esi: (addr environment) <- copy _self + var function/edi: (addr function) <- copy _function + var cursor-word-ah/ebx: (addr handle word) <- get function, cursor-word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + var cursor-word/ecx: (addr word) <- copy _cursor-word + compare key, 0x445b1b/left-arrow + $process-function-edit:key-left-arrow: { + break-if-!= +#? print-string 0, "left-arrow\n" + # if not at start, move left within current word + var at-start?/eax: boolean <- cursor-at-start? cursor-word + compare at-start?, 0/false + { + break-if-!= +#? print-string 0, "cursor left within word\n" + cursor-left cursor-word + return + } + # otherwise move to end of previous word + var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + { + compare prev-word, 0 + break-if-= +#? print-string 0, "move to previous word\n" + cursor-to-end prev-word + copy-object prev-word-ah, cursor-word-ah + } + return + } + compare key, 0x435b1b/right-arrow + $process-function-edit:key-right-arrow: { + break-if-!= + # if not at end, move right within current word + var at-end?/eax: boolean <- cursor-at-end? cursor-word + compare at-end?, 0/false + { + break-if-!= + cursor-right cursor-word + return + } + # otherwise, move to the next word + var next-word-ah/edx: (addr handle word) <- get cursor-word, next + var next-word/eax: (addr word) <- lookup *next-word-ah + { + compare next-word, 0 + break-if-= + cursor-to-start next-word + copy-object next-word-ah, cursor-word-ah + } + return + } + # word-based motions + compare key, 2/ctrl-b + $process-function-edit:prev-word: { + break-if-!= + # jump to previous word if possible + var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + { + compare prev-word, 0 + break-if-= + cursor-to-end prev-word + copy-object prev-word-ah, cursor-word-ah + } + return + } + compare key, 6/ctrl-f + $process-function-edit:next-word: { + break-if-!= + # jump to previous word if possible + var next-word-ah/edx: (addr handle word) <- get cursor-word, next + var next-word/eax: (addr word) <- lookup *next-word-ah + { + compare next-word, 0 + break-if-= + cursor-to-end next-word + copy-object next-word-ah, cursor-word-ah + } + return + } + # line-based motions + compare key, 1/ctrl-a + $process-function-edit:start-of-line: { + break-if-!= + # move cursor to start of first word + var body-ah/eax: (addr handle line) <- get function, body + var body/eax: (addr line) <- lookup *body-ah + var body-contents-ah/ecx: (addr handle word) <- get body, data + copy-object body-contents-ah, cursor-word-ah + var body-contents/eax: (addr word) <- lookup *body-contents-ah + cursor-to-start body-contents + return + } + compare key, 5/ctrl-e + $process-function-edit:end-of-line: { + break-if-!= + # move cursor to end of final word + var body-ah/eax: (addr handle line) <- get function, body + var body/eax: (addr line) <- lookup *body-ah + var body-contents-ah/ecx: (addr handle word) <- get body, data + copy-object body-contents-ah, cursor-word-ah + final-word cursor-word-ah, cursor-word-ah + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-end cursor-word + return + } + # bounce to another function + compare key, 7/ctrl-g + $process-function-edit:goto-function: { + break-if-!= + # initialize dialog to name function to jump to + var partial-function-name-ah/eax: (addr handle word) <- get self, partial-function-name + allocate partial-function-name-ah + var partial-function-name/eax: (addr word) <- lookup *partial-function-name-ah + initialize-word partial-function-name + return + } + # bounce to sandbox + compare key, 9/tab + $process-function-edit:goto-sandbox: { + break-if-!= + var function-ah/eax: (addr handle function) <- get self, cursor-function + clear-object function-ah + return + } + # editing the current function + compare key, 0x7f/del # backspace on Macs + $process-function-edit:backspace: { + break-if-!= + # if not at start of some word, delete grapheme before cursor within current word + var at-start?/eax: boolean <- cursor-at-start? cursor-word + compare at-start?, 0/false + { + break-if-!= + delete-before-cursor cursor-word + return + } + # otherwise delete current word and move to end of prev word + var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + { + compare prev-word, 0 + break-if-= + cursor-to-end prev-word + delete-next prev-word + copy-object prev-word-ah, cursor-word-ah + } + return + } + compare key, 0x20/space + $process-function-edit:space: { + break-if-!= +#? print-string 0, "space\n" + # if cursor is at start of word, insert word before + { + var at-start?/eax: boolean <- cursor-at-start? cursor-word + compare at-start?, 0/false + break-if-= + var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev + append-word prev-word-ah + var new-prev-word-ah/eax: (addr handle word) <- get cursor-word, prev + copy-object new-prev-word-ah, cursor-word-ah + return + } + # if start of word is quote and grapheme before cursor is not, just insert it as usual + # TODO: support string escaping + { + var first-grapheme/eax: grapheme <- first-grapheme cursor-word + compare first-grapheme, 0x22/double-quote + break-if-!= + var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word + compare final-grapheme, 0x22/double-quote + break-if-= + break $process-function-edit:space + } + # if start of word is '[' and grapheme before cursor is not ']', just insert it as usual + # TODO: support nested arrays + { + var first-grapheme/eax: grapheme <- first-grapheme cursor-word + compare first-grapheme, 0x5b/[ + break-if-!= + var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word + compare final-grapheme, 0x5d/] + break-if-= + break $process-function-edit:space + } + # otherwise insert word after and move cursor to it for the next key + # (but we'll continue to track the current cursor-word for the rest of this function) + append-word cursor-word-ah + var next-word-ah/eax: (addr handle word) <- get cursor-word, next + copy-object next-word-ah, cursor-word-ah + # if cursor is at end of word, that's all + var at-end?/eax: boolean <- cursor-at-end? cursor-word + compare at-end?, 0/false + { + break-if-= + return + } + # otherwise we're in the middle of a word + # move everything after cursor to the (just created) next word + var next-word-ah/eax: (addr handle word) <- get cursor-word, next + var _next-word/eax: (addr word) <- lookup *next-word-ah + var next-word/ebx: (addr word) <- copy _next-word + { + var at-end?/eax: boolean <- cursor-at-end? cursor-word + compare at-end?, 0/false + break-if-!= + var g/eax: grapheme <- pop-after-cursor cursor-word + add-grapheme-to-word next-word, g + loop + } + cursor-to-start next-word + return + } + # otherwise insert key within current word + var g/edx: grapheme <- copy key + var print?/eax: boolean <- real-grapheme? key + $process-function-edit:real-grapheme: { + compare print?, 0/false + break-if-= + add-grapheme-to-word cursor-word, g + return + } + # silently ignore other hotkeys +} + +fn process-sandbox _self: (addr environment), _sandbox: (addr sandbox), key: grapheme { + var self/esi: (addr environment) <- copy _self + var sandbox/edi: (addr sandbox) <- copy _sandbox + var rename-word-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-cursor-word + var rename-word-mode?/eax: (addr word) <- lookup *rename-word-mode-ah? + compare rename-word-mode?, 0 + { + break-if-= +#? print-string 0, "processing sandbox rename\n" + process-sandbox-rename sandbox, key + return + } + var define-function-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-function + var define-function-mode?/eax: (addr word) <- lookup *define-function-mode-ah? + compare define-function-mode?, 0 + { + break-if-= +#? print-string 0, "processing function definition\n" + var functions/ecx: (addr handle function) <- get self, functions + process-sandbox-define sandbox, functions, key + return + } +#? print-string 0, "processing sandbox edit\n" + process-sandbox-edit self, sandbox, key +} + +fn process-sandbox-edit _self: (addr environment), _sandbox: (addr sandbox), key: grapheme { + var self/esi: (addr environment) <- copy _self + var sandbox/edi: (addr sandbox) <- copy _sandbox + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/ebx: (addr handle word) <- get cursor-call-path, word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + var cursor-word/ecx: (addr word) <- copy _cursor-word + compare key, 0x445b1b/left-arrow + $process-sandbox-edit:key-left-arrow: { + break-if-!= +#? print-string 0, "left-arrow\n" + # if not at start, move left within current word + var at-start?/eax: boolean <- cursor-at-start? cursor-word + compare at-start?, 0/false + { + break-if-!= +#? print-string 0, "cursor left within word\n" + cursor-left cursor-word + return + } + # if current word is expanded, move to the rightmost word in its body + { + var cursor-call-path/esi: (addr handle call-path-element) <- get sandbox, cursor-call-path + var expanded-words/edx: (addr handle call-path) <- get sandbox, expanded-words + var curr-word-is-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path + compare curr-word-is-expanded?, 0/false + break-if-= + # update cursor-call-path +#? print-string 0, "curr word is expanded\n" + var self/ecx: (addr environment) <- copy _self + var functions/ecx: (addr handle function) <- get self, functions + var body: (handle line) + var body-ah/eax: (addr handle line) <- address body + function-body functions, cursor-word-ah, body-ah + var body-addr/eax: (addr line) <- lookup *body-ah + var first-word-ah/edx: (addr handle word) <- get body-addr, data + var final-word-h: (handle word) + var final-word-ah/eax: (addr handle word) <- address final-word-h + final-word first-word-ah, final-word-ah + push-to-call-path-element cursor-call-path, final-word-ah + # move cursor to end of word + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-end cursor-word + return + } + # if at first word, look for a caller to jump to + $process-sandbox-edit:key-left-arrow-first-word: { + var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + compare prev-word, 0 + break-if-!= + $process-sandbox-edit:key-left-arrow-first-word-and-caller: { +#? print-string 0, "return\n" + { + var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var next-cursor-element-ah/edx: (addr handle call-path-element) <- get cursor-call-path, next + var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah + compare next-cursor-element, 0 + break-if-= $process-sandbox-edit:key-left-arrow-first-word-and-caller + copy-object next-cursor-element-ah, cursor-call-path-ah + } + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-word <- copy _cursor-word + } + } + # then move to end of previous word + var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + { + compare prev-word, 0 + break-if-= +#? print-string 0, "move to previous word\n" + cursor-to-end prev-word +#? { +#? var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path +#? var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah +#? var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word +#? var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah +#? var cursor-word/ebx: (addr word) <- copy _cursor-word +#? print-string 0, "word at cursor before: " +#? print-word 0, cursor-word +#? print-string 0, "\n" +#? } + var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + decrement-final-element cursor-call-path +#? { +#? var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path +#? var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah +#? var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word +#? var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah +#? var cursor-word/ebx: (addr word) <- copy _cursor-word +#? print-string 0, "word at cursor after: " +#? print-word 0, cursor-word +#? print-string 0, "\n" +#? } + } + return + } + compare key, 0x435b1b/right-arrow + $process-sandbox-edit:key-right-arrow: { + break-if-!= + # if not at end, move right within current word + var at-end?/eax: boolean <- cursor-at-end? cursor-word + compare at-end?, 0/false + { + break-if-!= +#? print-string 0, "a\n" + cursor-right cursor-word + return + } + # if at final word, look for a caller to jump to + { + var next-word-ah/edx: (addr handle word) <- get cursor-word, next + var next-word/eax: (addr word) <- lookup *next-word-ah + compare next-word, 0 + break-if-!= + var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var next-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next + var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah + compare next-cursor-element, 0 + break-if-= + copy-object next-cursor-element-ah, cursor-call-path-ah + return + } + # otherwise, move to the next word + var next-word-ah/edx: (addr handle word) <- get cursor-word, next + var next-word/eax: (addr word) <- lookup *next-word-ah + { + compare next-word, 0 + break-if-= +#? print-string 0, "b\n" + cursor-to-start next-word + # . . cursor-word now out of date + var cursor-call-path/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path + increment-final-element cursor-call-path + # Is the new cursor word expanded? If so, it's a function call. Add a + # new level to the cursor-call-path for the call's body. + $process-sandbox-edit:key-right-arrow-next-word-is-call-expanded: { +#? print-string 0, "c\n" + { + var expanded-words/eax: (addr handle call-path) <- get sandbox, expanded-words + var curr-word-is-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path + compare curr-word-is-expanded?, 0/false + break-if-= $process-sandbox-edit:key-right-arrow-next-word-is-call-expanded + } + var callee-h: (handle function) + var callee-ah/edx: (addr handle function) <- address callee-h + var functions/ebx: (addr handle function) <- get self, functions + callee functions, next-word, callee-ah + var callee/eax: (addr function) <- lookup *callee-ah + var callee-body-ah/eax: (addr handle line) <- get callee, body + var callee-body/eax: (addr line) <- lookup *callee-body-ah + var callee-body-first-word/edx: (addr handle word) <- get callee-body, data + push-to-call-path-element cursor-call-path, callee-body-first-word + # position cursor at left + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-start cursor-word +#? print-string 0, "d\n" + return + } + } + return + } + compare key, 0xa/enter + { + break-if-!= + # toggle display of subsidiary stack + toggle-cursor-word sandbox + return + } + compare key, 0xc/ctrl-l + $process-sandbox-edit:new-line: { + break-if-!= + # new line in sandbox + append-line sandbox + return + } + # word-based motions + compare key, 2/ctrl-b + $process-sandbox-edit:prev-word: { + break-if-!= + # jump to previous word at same level + var prev-word-ah/edx: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + { + compare prev-word, 0 + break-if-= + cursor-to-end prev-word + var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + decrement-final-element cursor-call-path + return + } + # if previous word doesn't exist, try to bump up one level + { + var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var caller-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next + var caller-cursor-element/eax: (addr call-path-element) <- lookup *caller-cursor-element-ah + compare caller-cursor-element, 0 + break-if-= + # check if previous word exists in caller + var caller-word-ah/eax: (addr handle word) <- get caller-cursor-element, word + var caller-word/eax: (addr word) <- lookup *caller-word-ah + var word-before-caller-ah/eax: (addr handle word) <- get caller-word, prev + var word-before-caller/eax: (addr word) <- lookup *word-before-caller-ah + compare word-before-caller, 0 + break-if-= + # if so jump to it + drop-from-call-path-element cursor-call-path-ah + decrement-final-element cursor-call-path-ah + return + } + } + compare key, 6/ctrl-f + $process-sandbox-edit:next-word: { + break-if-!= +#? print-string 0, "AA\n" + # jump to previous word at same level + var next-word-ah/edx: (addr handle word) <- get cursor-word, next + var next-word/eax: (addr word) <- lookup *next-word-ah + { + compare next-word, 0 + break-if-= +#? print-string 0, "BB\n" + cursor-to-end next-word + var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + increment-final-element cursor-call-path + return + } + # if next word doesn't exist, try to bump up one level +#? print-string 0, "CC\n" + var cursor-call-path-ah/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var caller-cursor-element-ah/ecx: (addr handle call-path-element) <- get cursor-call-path, next + var caller-cursor-element/eax: (addr call-path-element) <- lookup *caller-cursor-element-ah + compare caller-cursor-element, 0 + break-if-= +#? print-string 0, "DD\n" + copy-object caller-cursor-element-ah, cursor-call-path-ah + return + } + compare key, 7/ctrl-g + $process-sandbox-edit:goto-function: { + break-if-!= + # initialize dialog to name function to jump to + var partial-function-name-ah/eax: (addr handle word) <- get self, partial-function-name + allocate partial-function-name-ah + var partial-function-name/eax: (addr word) <- lookup *partial-function-name-ah + initialize-word partial-function-name + return + } + # line-based motions + compare key, 1/ctrl-a + $process-sandbox-edit:start-of-line: { + break-if-!= + # move cursor up past all calls and to start of line + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + drop-nested-calls cursor-call-path-ah + move-final-element-to-start-of-line cursor-call-path-ah + # move cursor to start of word + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-start cursor-word + # this works as long as the first word isn't expanded + # but we don't expect to see zero-arg functions first-up + return + } + compare key, 5/ctrl-e + $process-sandbox-edit:end-of-line: { + break-if-!= + # move cursor up past all calls and to start of line + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + drop-nested-calls cursor-call-path-ah + move-final-element-to-end-of-line cursor-call-path-ah + # move cursor to end of word + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-end cursor-word + # this works because expanded words lie to the right of their bodies + # so the final word is always guaranteed to be at the top-level + return + } + compare key, 0x15/ctrl-u + $process-sandbox-edit:clear-line: { + break-if-!= + # clear line in sandbox + initialize-sandbox sandbox + return + } + # if cursor is within a call, disable editing hotkeys below + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var next-cursor-element-ah/eax: (addr handle call-path-element) <- get cursor-call-path, next + var next-cursor-element/eax: (addr call-path-element) <- lookup *next-cursor-element-ah + compare next-cursor-element, 0 + { + break-if-= + return + } + # - remaining keys only work at the top row outside any function calls + compare key, 0x7f/del # backspace on Macs + $process-sandbox-edit:backspace: { + break-if-!= + # if not at start of some word, delete grapheme before cursor within current word + var at-start?/eax: boolean <- cursor-at-start? cursor-word + compare at-start?, 0/false + { + break-if-!= + delete-before-cursor cursor-word + return + } + # otherwise delete current word and move to end of prev word + var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + { + compare prev-word, 0 + break-if-= + cursor-to-end prev-word + delete-next prev-word + var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + decrement-final-element cursor-call-path + } + return + } + compare key, 0x20/space + $process-sandbox-edit:space: { + break-if-!= +#? print-string 0, "space\n" + # if cursor is at start of word, insert word before + { + var at-start?/eax: boolean <- cursor-at-start? cursor-word + compare at-start?, 0/false + break-if-= + var prev-word-ah/eax: (addr handle word) <- get cursor-word, prev + append-word prev-word-ah + var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + decrement-final-element cursor-call-path + return + } + # if start of word is quote and grapheme before cursor is not, just insert it as usual + # TODO: support string escaping + { + var first-grapheme/eax: grapheme <- first-grapheme cursor-word + compare first-grapheme, 0x22/double-quote + break-if-!= + var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word + compare final-grapheme, 0x22/double-quote + break-if-= + break $process-sandbox-edit:space + } + # if start of word is '[' and grapheme before cursor is not ']', just insert it as usual + # TODO: support nested arrays + { + var first-grapheme/eax: grapheme <- first-grapheme cursor-word + compare first-grapheme, 0x5b/[ + break-if-!= + var final-grapheme/eax: grapheme <- grapheme-before-cursor cursor-word + compare final-grapheme, 0x5d/] + break-if-= + break $process-sandbox-edit:space + } + # otherwise insert word after and move cursor to it for the next key + # (but we'll continue to track the current cursor-word for the rest of this function) + append-word cursor-word-ah + var cursor-call-path/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + increment-final-element cursor-call-path + # if cursor is at end of word, that's all + var at-end?/eax: boolean <- cursor-at-end? cursor-word + compare at-end?, 0/false + { + break-if-= + return + } + # otherwise we're in the middle of a word + # move everything after cursor to the (just created) next word + var next-word-ah/eax: (addr handle word) <- get cursor-word, next + var _next-word/eax: (addr word) <- lookup *next-word-ah + var next-word/ebx: (addr word) <- copy _next-word + { + var at-end?/eax: boolean <- cursor-at-end? cursor-word + compare at-end?, 0/false + break-if-!= + var g/eax: grapheme <- pop-after-cursor cursor-word + add-grapheme-to-word next-word, g + loop + } + cursor-to-start next-word + return + } + compare key, 0xe/ctrl-n + $process:rename-word: { + break-if-!= + # TODO: ensure current word is not a function + # rename word at cursor + var new-name-ah/eax: (addr handle word) <- get sandbox, partial-name-for-cursor-word + allocate new-name-ah + var new-name/eax: (addr word) <- lookup *new-name-ah + initialize-word new-name + return + } + compare key, 4/ctrl-d + $process:define-function: { + break-if-!= + # define function out of line at cursor + var new-name-ah/eax: (addr handle word) <- get sandbox, partial-name-for-function + allocate new-name-ah + var new-name/eax: (addr word) <- lookup *new-name-ah + initialize-word new-name + return + } + # otherwise insert key within current word + var g/edx: grapheme <- copy key + var print?/eax: boolean <- real-grapheme? key + $process-sandbox-edit:real-grapheme: { + compare print?, 0/false + break-if-= + add-grapheme-to-word cursor-word, g + return + } + # silently ignore other hotkeys +} + +# collect new name in partial-name-for-cursor-word, and then rename the word +# at cursor to it +# Precondition: cursor-call-path is a singleton (not within a call) +fn process-sandbox-rename _sandbox: (addr sandbox), key: grapheme { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var new-name-ah/edi: (addr handle word) <- get sandbox, partial-name-for-cursor-word + # if 'esc' pressed, cancel rename + compare key, 0x1b/esc + $process-sandbox-rename:cancel: { + break-if-!= + clear-object new-name-ah + return + } + # if 'enter' pressed, perform rename + compare key, 0xa/enter + $process-sandbox-rename:commit: { + break-if-!= +#? print-string 0, "rename\n" + # new line + var new-line-h: (handle line) + var new-line-ah/eax: (addr handle line) <- address new-line-h + allocate new-line-ah + var new-line/eax: (addr line) <- lookup *new-line-ah + initialize-line new-line + var new-line-word-ah/ecx: (addr handle word) <- get new-line, data + { + # move word at cursor to new line + var cursor-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor/eax: (addr call-path-element) <- lookup *cursor-ah + var word-at-cursor-ah/eax: (addr handle word) <- get cursor, word +#? print-string 0, "cursor before at word " +#? { +#? var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah +#? print-word 0, cursor-word +#? print-string 0, "\n" +#? } + move-word-contents word-at-cursor-ah, new-line-word-ah + # copy name to word at cursor + copy-word-contents-before-cursor new-name-ah, word-at-cursor-ah +#? print-string 0, "cursor after at word " +#? { +#? var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah +#? print-word 0, cursor-word +#? print-string 0, "\n" +#? var foo/eax: int <- copy cursor-word +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } +#? print-string 0, "new name word " +#? { +#? var new-name/eax: (addr word) <- lookup *new-name-ah +#? print-word 0, new-name +#? print-string 0, "\n" +#? var foo/eax: int <- copy new-name +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + } + # prepend '=' to name + { + var new-name/eax: (addr word) <- lookup *new-name-ah + cursor-to-start new-name + add-grapheme-to-word new-name, 0x3d/= + } + # append name to new line + chain-words new-line-word-ah, new-name-ah + # new-line->next = sandbox->data + var new-line-next/ecx: (addr handle line) <- get new-line, next + var sandbox-slot/edx: (addr handle line) <- get sandbox, data + copy-object sandbox-slot, new-line-next + # sandbox->data = new-line + copy-handle new-line-h, sandbox-slot + # clear partial-name-for-cursor-word + clear-object new-name-ah +#? var cursor-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path +#? var cursor/eax: (addr call-path-element) <- lookup *cursor-ah +#? var word-at-cursor-ah/eax: (addr handle word) <- get cursor, word +#? print-string 0, "cursor after rename: " +#? { +#? var cursor-word/eax: (addr word) <- lookup *word-at-cursor-ah +#? print-word 0, cursor-word +#? print-string 0, " -- " +#? var foo/eax: int <- copy cursor-word +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + return + } + # + compare key, 0x7f/del # backspace on Macs + $process-sandbox-rename:backspace: { + break-if-!= + # if not at start, delete grapheme before cursor + var new-name/eax: (addr word) <- lookup *new-name-ah + var at-start?/eax: boolean <- cursor-at-start? new-name + compare at-start?, 0/false + { + break-if-!= + var new-name/eax: (addr word) <- lookup *new-name-ah + delete-before-cursor new-name + } + return + } + # otherwise insert key within current word + var print?/eax: boolean <- real-grapheme? key + $process-sandbox-rename:real-grapheme: { + compare print?, 0/false + break-if-= + var new-name/eax: (addr word) <- lookup *new-name-ah + add-grapheme-to-word new-name, key + return + } + # silently ignore other hotkeys +} + +# collect new name in partial-name-for-function, and then define the last line +# of the sandbox to be a new function with that name. Replace the last line +# with a call to the appropriate function. +# Precondition: cursor-call-path is a singleton (not within a call) +fn process-sandbox-define _sandbox: (addr sandbox), functions: (addr handle function), key: grapheme { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var new-name-ah/edi: (addr handle word) <- get sandbox, partial-name-for-function + # if 'esc' pressed, cancel define + compare key, 0x1b/esc + $process-sandbox-define:cancel: { + break-if-!= + clear-object new-name-ah + return + } + # if 'enter' pressed, perform define + compare key, 0xa/enter + $process-sandbox-define:commit: { + break-if-!= +#? print-string 0, "define\n" + # create new function + var new-function: (handle function) + var new-function-ah/ecx: (addr handle function) <- address new-function + allocate new-function-ah + var _new-function/eax: (addr function) <- lookup *new-function-ah + var new-function/ebx: (addr function) <- copy _new-function + var dest/edx: (addr handle function) <- get new-function, next + copy-object functions, dest + copy-object new-function-ah, functions + # set function name to new-name + var new-name/eax: (addr word) <- lookup *new-name-ah + var dest/edx: (addr handle array byte) <- get new-function, name + word-to-string new-name, dest + # move final line to body + var body-ah/eax: (addr handle line) <- get new-function, body + allocate body-ah + var body/eax: (addr line) <- lookup *body-ah + var body-contents/ecx: (addr handle word) <- get body, data + var final-line-storage: (handle line) + var final-line-ah/eax: (addr handle line) <- address final-line-storage + final-line sandbox, final-line-ah + var final-line/eax: (addr line) <- lookup *final-line-ah + var final-line-contents/eax: (addr handle word) <- get final-line, data + copy-object final-line-contents, body-contents + var cursor-word-ah/ecx: (addr handle word) <- get new-function, cursor-word + copy-object final-line-contents, cursor-word-ah + { + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-start cursor-word + } + # + copy-unbound-words-to-args functions + # + var empty-word: (handle word) + copy-handle empty-word, final-line-contents + construct-call functions, final-line-contents + # clear partial-name-for-function + var empty-word: (handle word) + copy-handle empty-word, new-name-ah + # update cursor + var final-line/eax: (addr line) <- lookup final-line-storage + var cursor-call-path-ah/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path + allocate cursor-call-path-ah # leak + initialize-path-from-line final-line, cursor-call-path-ah + return + } + # + compare key, 0x7f/del # backspace on Macs + $process-sandbox-define:backspace: { + break-if-!= + # if not at start, delete grapheme before cursor + var new-name/eax: (addr word) <- lookup *new-name-ah + var at-start?/eax: boolean <- cursor-at-start? new-name + compare at-start?, 0/false + { + break-if-!= + var new-name/eax: (addr word) <- lookup *new-name-ah + delete-before-cursor new-name + } + return + } + # otherwise insert key within current word + var print?/eax: boolean <- real-grapheme? key + $process-sandbox-define:real-grapheme: { + compare print?, 0/false + break-if-= + var new-name/eax: (addr word) <- lookup *new-name-ah + add-grapheme-to-word new-name, key + return + } + # silently ignore other hotkeys +} + +# extract from the body of the first function in 'functions' all words that +# aren't defined in the rest of 'functions'. Prepend them in reverse order. +# Assumes function body is a single line for now. +fn copy-unbound-words-to-args _functions: (addr handle function) { + # target + var target-ah/eax: (addr handle function) <- copy _functions + var _target/eax: (addr function) <- lookup *target-ah + var target/esi: (addr function) <- copy _target + var dest-ah/edi: (addr handle word) <- get target, args + # next + var functions-ah/edx: (addr handle function) <- get target, next + # src + var line-ah/eax: (addr handle line) <- get target, body + var line/eax: (addr line) <- lookup *line-ah + var curr-ah/eax: (addr handle word) <- get line, data + var curr/eax: (addr word) <- lookup *curr-ah + { + compare curr, 0 + break-if-= + $copy-unbound-words-to-args:loop-iter: { + # is it a number? + { + var is-int?/eax: boolean <- word-is-decimal-integer? curr + compare is-int?, 0/false + break-if-!= $copy-unbound-words-to-args:loop-iter + } + # is it a pre-existing function? + var bound?/ebx: boolean <- bound-function? curr, functions-ah + compare bound?, 0/false + break-if-!= + # is it already bound as an arg? + var dup?/ebx: boolean <- arg-exists? _functions, curr # _functions = target-ah + compare dup?, 0/false + break-if-!= $copy-unbound-words-to-args:loop-iter + # push copy of curr before dest-ah + var rest-h: (handle word) + var rest-ah/ecx: (addr handle word) <- address rest-h + copy-object dest-ah, rest-ah + copy-word curr, dest-ah + chain-words dest-ah, rest-ah + } + var next-ah/ecx: (addr handle word) <- get curr, next + curr <- lookup *next-ah + loop + } +} + +fn bound-function? w: (addr word), functions-ah: (addr handle function) -> _/ebx: boolean { + var result/ebx: boolean <- copy 1/true + { + ## numbers + # if w == "+" return true + var subresult/eax: boolean <- word-equal? w, "+" + compare subresult, 0/false + break-if-!= + # if w == "-" return true + subresult <- word-equal? w, "-" + compare subresult, 0/false + break-if-!= + # if w == "*" return true + subresult <- word-equal? w, "*" + compare subresult, 0/false + break-if-!= + # if w == "/" return true + subresult <- word-equal? w, "/" + compare subresult, 0/false + break-if-!= + # if w == "sqrt" return true + subresult <- word-equal? w, "sqrt" + compare subresult, 0/false + break-if-!= + ## strings/arrays + # if w == "len" return true + subresult <- word-equal? w, "len" + compare subresult, 0/false + break-if-!= + ## files + # if w == "open" return true + subresult <- word-equal? w, "open" + compare subresult, 0/false + break-if-!= + # if w == "read" return true + subresult <- word-equal? w, "read" + compare subresult, 0/false + break-if-!= + # if w == "slurp" return true + subresult <- word-equal? w, "slurp" + compare subresult, 0/false + break-if-!= + # if w == "lines" return true + subresult <- word-equal? w, "lines" + compare subresult, 0/false + break-if-!= + ## screens + # if w == "fake-screen" return true + subresult <- word-equal? w, "fake-screen" + compare subresult, 0/false + break-if-!= + # if w == "print" return true + subresult <- word-equal? w, "print" + compare subresult, 0/false + break-if-!= + # if w == "move" return true + subresult <- word-equal? w, "move" + compare subresult, 0/false + break-if-!= + # if w == "up" return true + subresult <- word-equal? w, "up" + compare subresult, 0/false + break-if-!= + # if w == "down" return true + subresult <- word-equal? w, "down" + compare subresult, 0/false + break-if-!= + # if w == "left" return true + subresult <- word-equal? w, "left" + compare subresult, 0/false + break-if-!= + # if w == "right" return true + subresult <- word-equal? w, "right" + compare subresult, 0/false + break-if-!= + ## hacks + # if w == "dup" return true + subresult <- word-equal? w, "dup" + compare subresult, 0/false + break-if-!= + # if w == "swap" return true + subresult <- word-equal? w, "swap" + compare subresult, 0/false + break-if-!= + # return w in functions + var out-h: (handle function) + var out/eax: (addr handle function) <- address out-h + callee functions-ah, w, out + var found?/eax: (addr function) <- lookup *out + result <- copy found? + } + return result +} + +fn arg-exists? _f-ah: (addr handle function), arg: (addr word) -> _/ebx: boolean { + var f-ah/eax: (addr handle function) <- copy _f-ah + var f/eax: (addr function) <- lookup *f-ah + var args-ah/eax: (addr handle word) <- get f, args + var result/ebx: boolean <- word-exists? args-ah, arg + return result +} + +# construct a call to `f` with copies of exactly its args +fn construct-call _f-ah: (addr handle function), _dest-ah: (addr handle word) { + var f-ah/eax: (addr handle function) <- copy _f-ah + var _f/eax: (addr function) <- lookup *f-ah + var f/esi: (addr function) <- copy _f + # append args in reverse + var args-ah/eax: (addr handle word) <- get f, args + var dest-ah/edi: (addr handle word) <- copy _dest-ah + copy-words-in-reverse args-ah, dest-ah + # append name + var name-ah/eax: (addr handle array byte) <- get f, name + var name/eax: (addr array byte) <- lookup *name-ah + append-word-at-end-with dest-ah, name +} + +fn word-index _words: (addr handle word), _n: int, out: (addr handle word) { + var n/ecx: int <- copy _n + { + compare n, 0 + break-if-!= + copy-object _words, out + return + } + var words-ah/eax: (addr handle word) <- copy _words + var words/eax: (addr word) <- lookup *words-ah + var next/eax: (addr handle word) <- get words, next + n <- decrement + word-index next, n, out +} + +fn toggle-cursor-word _sandbox: (addr sandbox) { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var expanded-words/edi: (addr handle call-path) <- get sandbox, expanded-words + var cursor-call-path/ecx: (addr handle call-path-element) <- get sandbox, cursor-call-path +#? print-string 0, "cursor call path: " +#? dump-call-path-element 0, cursor-call-path +#? print-string 0, "expanded words:\n" +#? dump-call-paths 0, expanded-words + var already-expanded?/eax: boolean <- find-in-call-paths expanded-words, cursor-call-path + compare already-expanded?, 0/false + { + break-if-!= +#? print-string 0, "expand\n" + # if not already-expanded, insert + insert-in-call-path expanded-words cursor-call-path +#? print-string 0, "expanded words now:\n" +#? dump-call-paths 0, expanded-words + return + } + { + break-if-= + # otherwise delete + delete-in-call-path expanded-words cursor-call-path + } +} + +fn append-line _sandbox: (addr sandbox) { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var line-ah/ecx: (addr handle line) <- get sandbox, data + { + var line/eax: (addr line) <- lookup *line-ah + var next-line-ah/edx: (addr handle line) <- get line, next + var next-line/eax: (addr line) <- lookup *next-line-ah + compare next-line, 0 + break-if-= + line-ah <- copy next-line-ah + loop + } + var line/eax: (addr line) <- lookup *line-ah + var final-line-ah/edx: (addr handle line) <- get line, next + allocate final-line-ah + var final-line/eax: (addr line) <- lookup *final-line-ah + initialize-line final-line + var final-prev/eax: (addr handle line) <- get final-line, prev + copy-object line-ah, final-prev + # clear cursor + var final-line/eax: (addr line) <- lookup *final-line-ah + var word-ah/ecx: (addr handle word) <- get final-line, data + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var dest/eax: (addr handle word) <- get cursor-call-path, word + copy-object word-ah, dest +} + +############# +# Visualize +############# + +fn evaluate-environment _env: (addr environment), stack: (addr value-stack) { + var env/esi: (addr environment) <- copy _env + # functions + var functions/edx: (addr handle function) <- get env, functions + # line + var sandbox-ah/esi: (addr handle sandbox) <- get env, sandboxes + var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah + var line-ah/eax: (addr handle line) <- get sandbox, data + var _line/eax: (addr line) <- lookup *line-ah + var line/esi: (addr line) <- copy _line + evaluate functions, 0, line, 0, stack +} + +fn render _env: (addr environment) { +#? print-string 0, "== render\n" + var env/esi: (addr environment) <- copy _env + clear-canvas env + # menu + render-menu env + # screen + var screen-ah/eax: (addr handle screen) <- get env, screen + var _screen/eax: (addr screen) <- lookup *screen-ah + var screen/edi: (addr screen) <- copy _screen + # functions + var sep-col/eax: (addr int) <- get env, code-separator-col + var functions/edx: (addr handle function) <- get env, functions + render-functions screen, *sep-col, env + # sandbox + var repl-col/ecx: int <- copy *sep-col + repl-col <- add 2/repl-margin-left + var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox + var cursor-sandbox/eax: (addr sandbox) <- lookup *cursor-sandbox-ah + # bindings + var bindings-storage: table + var bindings/ebx: (addr table) <- address bindings-storage + initialize-table bindings, 0x10 +#? print-string 0, "render-sandbox {\n" + render-sandbox screen, functions, bindings, cursor-sandbox, 3, repl-col +#? print-string 0, "render-sandbox }\n" + # dialogs + render-goto-dialog screen, env + # + position-cursor screen, env +} + +# draw a wordstar-style cheatsheet of shortcuts on the bottom line of the screen +fn render-menu _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + var cursor-function-ah/eax: (addr handle function) <- get env, cursor-function + var cursor-function/eax: (addr function) <- lookup *cursor-function-ah + { + compare cursor-function, 0 + break-if-= + render-function-menu env + return + } + var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox + var cursor-sandbox/eax: (addr sandbox) <- lookup *cursor-sandbox-ah + { + compare cursor-sandbox, 0 + break-if-= + render-sandbox-menu env + return + } +} + +# HACK: areas currently responsible for positioning their dialogs' cursors. So +# we just do nothing here if a dialog is up. +fn position-cursor screen: (addr screen), _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + var goto-function-ah/eax: (addr handle word) <- get env, partial-function-name + var goto-function/eax: (addr word) <- lookup *goto-function-ah + { + compare goto-function, 0 + break-if-= + return + } + var cursor-function-ah/eax: (addr handle function) <- get env, cursor-function + var cursor-function/eax: (addr function) <- lookup *cursor-function-ah + { + compare cursor-function, 0 + break-if-= + var cursor-row/ecx: (addr int) <- get cursor-function, cursor-row + var cursor-col/eax: (addr int) <- get cursor-function, cursor-col + move-cursor screen, *cursor-row, *cursor-col + return + } + var cursor-sandbox-ah/eax: (addr handle sandbox) <- get env, cursor-sandbox + var cursor-sandbox/eax: (addr sandbox) <- lookup *cursor-sandbox-ah + { + compare cursor-sandbox, 0 + break-if-= + # if in a dialog, return + { + var partial-word-rename-ah/eax: (addr handle word) <- get cursor-sandbox, partial-name-for-cursor-word + var partial-word-rename/eax: (addr word) <- lookup *partial-word-rename-ah + compare partial-word-rename, 0 + break-if-= + return + } + { + var partial-function-name-ah/eax: (addr handle word) <- get cursor-sandbox, partial-name-for-function + var partial-function-name/eax: (addr word) <- lookup *partial-function-name-ah + compare partial-function-name, 0 + break-if-= + return + } + var cursor-row/ecx: (addr int) <- get cursor-sandbox, cursor-row + var cursor-col/eax: (addr int) <- get cursor-sandbox, cursor-col + move-cursor screen, *cursor-row, *cursor-col + } +} + +fn render-goto-dialog screen: (addr screen), _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + var goto-function-mode-ah?/eax: (addr handle word) <- get env, partial-function-name + var goto-function-mode?/eax: (addr word) <- lookup *goto-function-mode-ah? + compare goto-function-mode?, 0/false + break-if-= + # clear a space for the dialog + var top-row/ebx: int <- copy 3 + var bottom-row/edx: int <- copy 9 + var sep-col/eax: (addr int) <- get env, code-separator-col + var left-col/ecx: int <- copy *sep-col + left-col <- subtract 0x10 + var right-col/eax: int <- copy *sep-col + right-col <- add 0x10 + clear-rect screen, top-row, left-col, bottom-row, right-col + draw-box screen, top-row, left-col, bottom-row, right-col + # render a little menu for the dialog + var menu-row/eax: int <- copy bottom-row + menu-row <- decrement + var menu-col/edx: int <- copy left-col + menu-col <- add 2 + move-cursor screen, menu-row, menu-col + start-reverse-video screen + print-string screen, " esc " + reset-formatting screen + print-string screen, " cancel " + start-reverse-video screen + print-string screen, " enter " + reset-formatting screen + print-string screen, " jump " + # draw the word, positioned appropriately around the cursor + var start-col/ecx: int <- copy left-col + start-col <- increment + move-cursor screen, 6, start-col # cursor-row + var word-ah?/edx: (addr handle word) <- get env, partial-function-name + var word/eax: (addr word) <- lookup *word-ah? + print-word screen, word +} + +fn render-sandbox screen: (addr screen), functions: (addr handle function), bindings: (addr table), _sandbox: (addr sandbox), top-row: int, left-col: int { + var sandbox/esi: (addr sandbox) <- copy _sandbox + # line + var curr-line-ah/eax: (addr handle line) <- get sandbox, data + var _curr-line/eax: (addr line) <- lookup *curr-line-ah + var curr-line/ecx: (addr line) <- copy _curr-line + # + var curr-row/edx: int <- copy top-row + # cursor row, col + var cursor-row-addr: (addr int) + var tmp/eax: (addr int) <- get sandbox, cursor-row + copy-to cursor-row-addr, tmp + var cursor-col-addr: (addr int) + tmp <- get sandbox, cursor-col + copy-to cursor-col-addr, tmp + # render all but final line without stack +#? print-string 0, "render all but final line\n" + { + var next-line-ah/eax: (addr handle line) <- get curr-line, next + var next-line/eax: (addr line) <- lookup *next-line-ah + compare next-line, 0 + break-if-= + { + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + # 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 + render-line-without-stack screen, curr-line, curr-row, left-col, cursor-word, cursor-row-addr, cursor-col-addr + } + curr-line <- copy next-line + curr-row <- add 2 + loop + } + # + render-final-line-with-stack screen, functions, bindings, sandbox, curr-row, left-col, cursor-row-addr, cursor-col-addr + # at most one of the following dialogs will be rendered + render-rename-dialog screen, sandbox + render-define-dialog screen, sandbox +} + +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) { + var sandbox/esi: (addr sandbox) <- copy _sandbox + # expanded-words + var expanded-words/edi: (addr handle call-path) <- get sandbox, expanded-words + # cursor-word + var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah + var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + var cursor-word/ebx: (addr word) <- copy _cursor-word +#? print-string 0, "word at cursor: " +#? print-word 0, cursor-word +#? print-string 0, "\n" + # cursor-call-path + var cursor-call-path: (addr handle call-path-element) + { + var src/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path + copy-to cursor-call-path, src + } + # first line + var first-line-ah/eax: (addr handle line) <- get sandbox, data + var _first-line/eax: (addr line) <- lookup *first-line-ah + var first-line/edx: (addr line) <- copy _first-line + # final line + var final-line-storage: (handle line) + var final-line-ah/eax: (addr handle line) <- address final-line-storage + final-line sandbox, final-line-ah + var final-line/eax: (addr line) <- lookup *final-line-ah + # curr-path + var curr-path-storage: (handle call-path-element) + var curr-path/ecx: (addr handle call-path-element) <- address curr-path-storage + allocate curr-path # leak + initialize-path-from-line final-line, curr-path + # + 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 +} + +fn final-line _sandbox: (addr sandbox), out: (addr handle line) { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var curr-line-ah/ecx: (addr handle line) <- get sandbox, data + { + var curr-line/eax: (addr line) <- lookup *curr-line-ah + var next-line-ah/edx: (addr handle line) <- get curr-line, next + var next-line/eax: (addr line) <- lookup *next-line-ah + compare next-line, 0 + break-if-= + curr-line-ah <- copy next-line-ah + loop + } + copy-object curr-line-ah, out +} + +fn render-rename-dialog screen: (addr screen), _sandbox: (addr sandbox) { + var sandbox/edi: (addr sandbox) <- copy _sandbox + var rename-word-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-cursor-word + var rename-word-mode?/eax: (addr word) <- lookup *rename-word-mode-ah? + compare rename-word-mode?, 0/false + break-if-= + # clear a space for the dialog + var cursor-row/ebx: (addr int) <- get sandbox, cursor-row + var top-row/eax: int <- copy *cursor-row + top-row <- subtract 3 + var bottom-row/ecx: int <- copy *cursor-row + bottom-row <- add 3 + var cursor-col/ebx: (addr int) <- get sandbox, cursor-col + var left-col/edx: int <- copy *cursor-col + left-col <- subtract 0x10 + var right-col/ebx: int <- copy *cursor-col + right-col <- add 0x10 + clear-rect screen, top-row, left-col, bottom-row, right-col + draw-box screen, top-row, left-col, bottom-row, right-col + # render a little menu for the dialog + var menu-row/ecx: int <- copy bottom-row + menu-row <- decrement + var menu-col/edx: int <- copy left-col + menu-col <- add 2 + move-cursor screen, menu-row, menu-col + start-reverse-video screen + print-string screen, " esc " + reset-formatting screen + print-string screen, " cancel " + start-reverse-video screen + print-string screen, " enter " + reset-formatting screen + print-string screen, " rename " + # draw the word, positioned appropriately around the cursor + var cursor-col/ebx: (addr int) <- get sandbox, cursor-col + var start-col/ecx: int <- copy *cursor-col + var word-ah?/edx: (addr handle word) <- get sandbox, partial-name-for-cursor-word + var word/eax: (addr word) <- lookup *word-ah? + var cursor-index/eax: int <- cursor-index word + start-col <- subtract cursor-index + var cursor-row/ebx: (addr int) <- get sandbox, cursor-row + move-cursor screen, *cursor-row, start-col + var word/eax: (addr word) <- lookup *word-ah? + print-word screen, word +} + +fn render-define-dialog screen: (addr screen), _sandbox: (addr sandbox) { + var sandbox/edi: (addr sandbox) <- copy _sandbox + var define-function-mode-ah?/ecx: (addr handle word) <- get sandbox, partial-name-for-function + var define-function-mode?/eax: (addr word) <- lookup *define-function-mode-ah? + compare define-function-mode?, 0/false + break-if-= + # clear a space for the dialog + var cursor-row/ebx: (addr int) <- get sandbox, cursor-row + var top-row/eax: int <- copy *cursor-row + top-row <- subtract 3 + var bottom-row/ecx: int <- copy *cursor-row + bottom-row <- add 3 + var cursor-col/ebx: (addr int) <- get sandbox, cursor-col + var left-col/edx: int <- copy *cursor-col + left-col <- subtract 0x10 + var right-col/ebx: int <- copy *cursor-col + right-col <- add 0x10 + clear-rect screen, top-row, left-col, bottom-row, right-col + draw-box screen, top-row, left-col, bottom-row, right-col + # render a little menu for the dialog + var menu-row/ecx: int <- copy bottom-row + menu-row <- decrement + var menu-col/edx: int <- copy left-col + menu-col <- add 2 + move-cursor screen, menu-row, menu-col + start-reverse-video screen + print-string screen, " esc " + reset-formatting screen + print-string screen, " cancel " + start-reverse-video screen + print-string screen, " enter " + reset-formatting screen + print-string screen, " define " + # draw the word, positioned appropriately around the cursor + var cursor-col/ebx: (addr int) <- get sandbox, cursor-col + var start-col/ecx: int <- copy *cursor-col + var word-ah?/edx: (addr handle word) <- get sandbox, partial-name-for-function + var word/eax: (addr word) <- lookup *word-ah? + var cursor-index/eax: int <- cursor-index word + start-col <- subtract cursor-index + var cursor-row/ebx: (addr int) <- get sandbox, cursor-row + move-cursor screen, *cursor-row, start-col + var word/eax: (addr word) <- lookup *word-ah? + print-word screen, word +} + +# Render just the words in 'line'. +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) { + # curr-word + var line/eax: (addr line) <- copy _line + var first-word-ah/eax: (addr handle word) <- get line, data + var _curr-word/eax: (addr word) <- lookup *first-word-ah + var curr-word/esi: (addr word) <- copy _curr-word + # + # loop-carried dependency + var curr-col/ecx: int <- copy left-col + # + { + compare curr-word, 0 + break-if-= +#? print-string 0, "-- word in penultimate lines: " +#? { +#? var foo/eax: int <- copy curr-word +#? print-int32-hex 0, foo +#? } +#? print-string 0, "\n" + var old-col/edx: int <- copy curr-col + move-cursor screen, curr-row, curr-col + print-word screen, curr-word + { + var max-width/eax: int <- word-length curr-word + curr-col <- add max-width + curr-col <- add 1/margin-right + } + # cache cursor column if necessary + { + compare curr-word, cursor-word + break-if-!= +#? print-string 0, "Cursor at " +#? print-int32-decimal 0, curr-row +#? print-string 0, ", " +#? print-int32-decimal 0, old-col +#? print-string 0, "\n" +#? print-string 0, "contents: " +#? print-word 0, cursor-word +#? print-string 0, "\n" +#? { +#? var foo/eax: int <- copy cursor-word +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + var dest/ecx: (addr int) <- copy cursor-row-addr + var src/eax: int <- copy curr-row + copy-to *dest, src + dest <- copy cursor-col-addr + copy-to *dest, old-col + var cursor-index-in-word/eax: int <- cursor-index curr-word + add-to *dest, cursor-index-in-word + } + # loop update + var next-word-ah/edx: (addr handle word) <- get curr-word, next + var _curr-word/eax: (addr word) <- lookup *next-word-ah + curr-word <- copy _curr-word + loop + } +} + +fn call-depth-at-cursor _sandbox: (addr sandbox) -> _/eax: int { + var sandbox/esi: (addr sandbox) <- copy _sandbox + var cursor-call-path/edi: (addr handle call-path-element) <- get sandbox, cursor-call-path + var result/eax: int <- call-path-element-length cursor-call-path + result <- add 2 # input-row - 1 + return result +} + +fn call-path-element-length _x: (addr handle call-path-element) -> _/eax: int { + var curr-ah/ecx: (addr handle call-path-element) <- copy _x + var result/edi: int <- copy 0 + { + var curr/eax: (addr call-path-element) <- lookup *curr-ah + compare curr, 0 + break-if-= + curr-ah <- get curr, next + result <- increment + loop + } + return result +} + +# Render the line of words in line, along with the state of the stack under each word. +# Also render any expanded function calls using recursive calls. +# +# Along the way, compute the column the cursor should be positioned at (cursor-col-addr). +fn render-line screen: (addr screen), functions: (addr handle function), bindings: (addr table), first-line: (addr line), _line: (addr line), expanded-words: (addr handle call-path), top-row: int, left-col: int, curr-path: (addr handle call-path-element), cursor-word: (addr word), cursor-call-path: (addr handle call-path-element), cursor-row-addr: (addr int), cursor-col-addr: (addr int) -> _/ecx: int { +#? print-string 0, "render-line\n" +#? dump-table bindings + # curr-word + var line/esi: (addr line) <- copy _line + var first-word-ah/eax: (addr handle word) <- get line, data + var curr-word/eax: (addr word) <- lookup *first-word-ah + # + # loop-carried dependency + var curr-col/ecx: int <- copy left-col + # + { + compare curr-word, 0 + break-if-= +#? print-string 0, "-- word " +#? print-word 0, curr-word +#? print-string 0, "\n" + # if necessary, first render columns for subsidiary stack + $render-line:subsidiary: { + { +#? print-string 0, "check sub\n" + var display-subsidiary-stack?/eax: boolean <- find-in-call-paths expanded-words, curr-path + compare display-subsidiary-stack?, 0/false + break-if-= $render-line:subsidiary + } +#? print-string 0, "render subsidiary stack\n" + # does function exist? + var callee/edi: (addr function) <- copy 0 + { + var callee-h: (handle function) + var callee-ah/ecx: (addr handle function) <- address callee-h + callee functions, curr-word, callee-ah + var _callee/eax: (addr function) <- lookup *callee-ah + callee <- copy _callee + compare callee, 0 + break-if-= $render-line:subsidiary + } + move-cursor screen, top-row, curr-col + start-color screen, 8, 7 + print-word screen, curr-word + { + var word-len/eax: int <- word-length curr-word + curr-col <- add word-len + curr-col <- add 2 + increment top-row + } + # obtain stack at call site + var stack-storage: value-stack + var stack/edx: (addr value-stack) <- address stack-storage + initialize-value-stack stack, 0x10 + { + var prev-word-ah/eax: (addr handle word) <- get curr-word, prev + var prev-word/eax: (addr word) <- lookup *prev-word-ah + compare prev-word, 0 + break-if-= + var bindings2-storage: table + var bindings2/ebx: (addr table) <- address bindings2-storage + deep-copy-table bindings, bindings2 + evaluate functions, bindings2, first-line, prev-word, stack + } + # construct new bindings + var callee-bindings-storage: table + var callee-bindings/esi: (addr table) <- address callee-bindings-storage + initialize-table callee-bindings, 0x10 + bind-args callee, stack, callee-bindings + # obtain body + var callee-body-ah/eax: (addr handle line) <- get callee, body + var callee-body/eax: (addr line) <- lookup *callee-body-ah + var callee-body-first-word/edx: (addr handle word) <- get callee-body, data + # - render subsidiary stack + push-to-call-path-element curr-path, callee-body-first-word # leak +#? print-string 0, "subsidiary {\n" +#? dump-table callee-bindings +#? syscall_exit + 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 +#? print-string 0, "}\n" + drop-from-call-path-element curr-path + # + move-cursor screen, top-row, curr-col + print-code-point screen, 0x21d7/⇗ + # + curr-col <- add 2 + decrement top-row + } + # render main column + var old-col/edx: int <- copy curr-col + var bindings2-storage: table + var bindings2/ebx: (addr table) <- address bindings2-storage +#? print-string 0, "deep-copy {\n" + deep-copy-table bindings, bindings2 +#? print-string 0, "}\n" +#? print-string 0, "render column {\n" + curr-col <- render-column screen, functions, bindings2, first-line, line, curr-word, top-row, curr-col +#? print-string 0, "}\n" + # cache cursor column if necessary + $render-line:cache-cursor-column: { + { + var found?/eax: boolean <- call-path-element-match? curr-path, cursor-call-path + compare found?, 0/false + break-if-= $render-line:cache-cursor-column + } + var dest/edi: (addr int) <- copy cursor-row-addr + { + var src/eax: int <- copy top-row + copy-to *dest, src + } + dest <- copy cursor-col-addr + copy-to *dest, old-col + var cursor-index-in-word/eax: int <- cursor-index curr-word + add-to *dest, cursor-index-in-word + } + # loop update +#? print-string 0, "next word\n" + var next-word-ah/edx: (addr handle word) <- get curr-word, next + curr-word <- lookup *next-word-ah +#? { +#? var foo/eax: int <- copy curr-word +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + increment-final-element curr-path + loop + } + return curr-col +} + +fn callee functions: (addr handle function), word: (addr word), out: (addr handle function) { + var stream-storage: (stream byte 0x10) + var stream/esi: (addr stream byte) <- address stream-storage + emit-word word, stream + find-function functions, stream, out +} + +# Render: +# - starting at top-row, left-col: final-word +# - starting somewhere below at left-col: the stack result from interpreting first-world to final-word (inclusive) +# +# Return the farthest column written. +fn render-column screen: (addr screen), functions: (addr handle function), bindings: (addr table), first-line: (addr line), line: (addr line), final-word: (addr word), top-row: int, left-col: int -> _/ecx: int { +#? print-string 0, "render-column\n" +#? dump-table bindings + var max-width/esi: int <- copy 0 + { + # compute stack + var stack: value-stack + var stack-addr/edi: (addr value-stack) <- address stack + initialize-value-stack stack-addr, 0x10/max-words + # copy bindings + var bindings2-storage: table + var bindings2/ebx: (addr table) <- address bindings2-storage +#? print-string 0, "deep copy table {\n" + deep-copy-table bindings, bindings2 +#? print-string 0, "}\n" + evaluate functions, bindings2, first-line, final-word, stack-addr + # indent stack + var indented-col/ebx: int <- copy left-col + indented-col <- add 1/margin-right + # render stack + var curr-row/edx: int <- copy top-row + curr-row <- add 2/stack-margin-top + { + var top-addr/ecx: (addr int) <- get stack-addr, top + compare *top-addr, 0 + break-if-<= + decrement *top-addr + var data-ah/eax: (addr handle array value) <- get stack-addr, data + var data/eax: (addr array value) <- lookup *data-ah + var top/ecx: int <- copy *top-addr + var dest-offset/ecx: (offset value) <- compute-offset data, top + var val/eax: (addr value) <- index data, dest-offset + render-value-at screen, curr-row, indented-col, val, 1/top-level=true + { + var width/eax: int <- value-width val, 1 + compare width, max-width + break-if-<= + max-width <- copy width + } + var height/eax: int <- value-height val + curr-row <- add height + loop + } + } + + max-width <- add 2 # spaces on either side of items on the stack + + # render word, initialize result + reset-formatting screen + move-cursor screen, top-row, left-col + print-word screen, final-word + { + var width/eax: int <- word-length final-word + compare width, max-width + break-if-<= + max-width <- copy width + } + + # post-process right-col + var right-col/ecx: int <- copy left-col + right-col <- add max-width + right-col <- add 1/margin-right +#? print-int32-decimal 0, left-col +#? print-string 0, " => " +#? print-int32-decimal 0, right-col +#? print-string 0, "\n" + return right-col +} + +fn render-function-menu _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + var screen-ah/edi: (addr handle screen) <- get env, screen + var _screen/eax: (addr screen) <- lookup *screen-ah + var screen/edi: (addr screen) <- copy _screen + var nrows/eax: (addr int) <- get env, nrows + move-cursor screen, *nrows, 0 + start-reverse-video screen + print-string screen, " ctrl-q " + reset-formatting screen + print-string screen, " quit " + start-reverse-video screen + print-string screen, " ctrl-a " + reset-formatting screen + print-string screen, " ⏮ " + start-reverse-video screen + print-string screen, " ctrl-b " + reset-formatting screen + print-string screen, " ◀ word " + start-reverse-video screen + print-string screen, " ctrl-f " + reset-formatting screen + print-string screen, " word ▶ " + start-reverse-video screen + print-string screen, " ctrl-e " + reset-formatting screen + print-string screen, " ⏭ " + start-reverse-video screen + print-string screen, " ctrl-g " + reset-formatting screen + print-string screen, " go to function " + start-reverse-video screen + print-string screen, " tab " + reset-formatting screen + print-string screen, " go to sandbox" +} + +fn render-sandbox-menu _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + var screen-ah/edi: (addr handle screen) <- get env, screen + var _screen/eax: (addr screen) <- lookup *screen-ah + var screen/edi: (addr screen) <- copy _screen + var nrows/eax: (addr int) <- get env, nrows + move-cursor screen, *nrows, 0 + start-reverse-video screen + print-string screen, " ctrl-q " + reset-formatting screen + print-string screen, " quit " + start-reverse-video screen + print-string screen, " ctrl-a " + reset-formatting screen + print-string screen, " ⏮ " + start-reverse-video screen + print-string screen, " ctrl-b " + reset-formatting screen + print-string screen, " ◀ word " + start-reverse-video screen + print-string screen, " ctrl-f " + reset-formatting screen + print-string screen, " word ▶ " + start-reverse-video screen + print-string screen, " ctrl-e " + reset-formatting screen + print-string screen, " ⏭ " + start-reverse-video screen + print-string screen, " ctrl-g " + reset-formatting screen + print-string screen, " go to function " + start-reverse-video screen + print-string screen, " ctrl-l " + reset-formatting screen + print-string screen, " new line " + start-reverse-video screen + print-string screen, " ctrl-u " + reset-formatting screen + print-string screen, " clear " + start-reverse-video screen + print-string screen, " ctrl-n " + reset-formatting screen + print-string screen, " name word " + start-reverse-video screen + print-string screen, " ctrl-d " + reset-formatting screen + print-string screen, " define function" +} + +fn clear-canvas _env: (addr environment) { + var env/esi: (addr environment) <- copy _env + var screen-ah/edi: (addr handle screen) <- get env, screen + var _screen/eax: (addr screen) <- lookup *screen-ah + var screen/edi: (addr screen) <- copy _screen + clear-screen screen + var nrows/eax: (addr int) <- get env, nrows + var sep-col/ecx: (addr int) <- get env, code-separator-col + # divider + draw-vertical-line screen, 1, *nrows, *sep-col + # primitives + var dummy/eax: int <- render-primitives screen, *nrows, *sep-col +} + +# return value: top-most row written to +fn render-primitives screen: (addr screen), bottom-margin-row: int, right-col: int -> _/eax: int { + # render primitives from the bottom of the screen upward + var row/ecx: int <- copy bottom-margin-row + row <- subtract 1 + var col/edx: int <- copy 1 + move-cursor screen, row, col + row, col <- render-primitive-group screen, row, col, right-col, "numbers: ", "+ - * / sqrt " + row, col <- render-primitive-group screen, row, col, right-col, "arrays: ", "len " + row, col <- render-primitive-group screen, row, col, right-col, "files: ", "open read slurp lines " + row, col <- render-primitive-group screen, row, col, right-col, "misc: ", "dup swap " # hack: keep these at the right of the bottom row + row, col <- render-primitive-group screen, row, col, right-col, "screens: ", "fake-screen print move up down left right " + # finally print heading up top + row <- decrement + move-cursor screen, row, 1 + start-bold screen + print-string screen, "primitives:" + reset-formatting screen + return row +} + +# start at row, col and print the given strings +# move up one row if there isn't enough room before right-col +# return row, col printed until +fn render-primitive-group screen: (addr screen), _row: int, _col: int, right-col: int, _heading: (addr array byte), _contents: (addr array byte) -> _/ecx: int, _/edx: int { + var row/ecx: int <- copy _row + var col/edx: int <- copy _col + # decrement row if necessary + var new-col/ebx: int <- copy col + var heading/esi: (addr array byte) <- copy _heading + var len1/eax: int <- length heading + new-col <- add len1 + var contents/edi: (addr array byte) <- copy _contents + var len2/eax: int <- length contents + new-col <- add len2 + var bound/eax: int <- copy right-col + bound <- decrement + { + compare new-col, bound + break-if-<= + row <- decrement + col <- copy 1 + } + move-cursor screen, row, col + start-color screen, 0xf6, 7 + print-string screen, heading + reset-formatting screen + print-string screen, contents + return row, new-col +} + +fn render-functions screen: (addr screen), right-col: int, _env: (addr environment) { + var row/ecx: int <- copy 1 + var dummy-col/edx: int <- copy right-col + var env/esi: (addr environment) <- copy _env + var functions/esi: (addr handle function) <- get env, functions + { + var curr/eax: (addr function) <- lookup *functions + compare curr, 0 + break-if-= + row, dummy-col <- render-function-right-aligned screen, row, right-col, curr + functions <- get curr, next + row <- add 1/inter-function-margin + loop + } +} + +# print function starting at row, right-aligned before right-col +# return row, col printed until +fn render-function-right-aligned screen: (addr screen), row: int, right-col: int, f: (addr function) -> _/ecx: int, _/edx: int { + var col/edx: int <- copy right-col + col <- subtract 1/function-right-margin + var col2/ebx: int <- copy col + var width/eax: int <- function-width f + col <- subtract width + var new-row/ecx: int <- copy row + var height/eax: int <- function-height f + new-row <- add height + new-row <- decrement + col <- subtract 1/function-left-padding + start-color screen, 0, 0xf7 + clear-rect screen, row, col, new-row, col2 + col <- add 1 +#? var dummy/eax: grapheme <- read-key-from-real-keyboard + render-function screen, row, col, f + new-row <- add 1/function-bottom-margin + col <- subtract 1/function-left-padding + col <- subtract 1/function-left-margin + reset-formatting screen + return new-row, col +} + +# render function starting at row, col +# only single-line functions supported for now +fn render-function screen: (addr screen), row: int, col: int, _f: (addr function) { + var f/esi: (addr function) <- copy _f + var args/ecx: (addr handle word) <- get f, args + move-cursor screen, row, col + print-words-in-reverse screen, args + var name-ah/eax: (addr handle array byte) <- get f, name + var name/eax: (addr array byte) <- lookup *name-ah + start-bold screen + print-string screen, name + reset-formatting screen + start-color screen, 0, 0xf7 + increment row + add-to col, 2 + move-cursor screen, row, col + print-string screen, "≡ " + add-to col, 2 + var cursor-row/ecx: (addr int) <- get f, cursor-row + var cursor-col/edx: (addr int) <- get f, cursor-col + var cursor-word-ah/eax: (addr handle word) <- get f, cursor-word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + var cursor-word/ebx: (addr word) <- copy _cursor-word + var body-ah/eax: (addr handle line) <- get f, body + var body/eax: (addr line) <- lookup *body-ah + render-line-without-stack screen, body, row, col, cursor-word, cursor-row, cursor-col +} + +fn real-grapheme? g: grapheme -> _/eax: boolean { + # if g == newline return true + compare g, 0xa + { + break-if-!= + return 1/true + } + # if g == tab return true + compare g, 9 + { + break-if-!= + return 1/true + } + # if g < 32 return false + compare g, 0x20 + { + break-if->= + return 0/false + } + # if g <= 255 return true + compare g, 0xff + { + break-if-> + return 1/true + } + # if (g&0xff == Esc) it's an escape sequence + and-with g, 0xff + compare g, 0x1b/esc + { + break-if-!= + return 0/false + } + # otherwise return true + return 1/true +} diff --git a/linux/tile/float-stack.mu b/linux/tile/float-stack.mu new file mode 100644 index 00000000..146995e7 --- /dev/null +++ b/linux/tile/float-stack.mu @@ -0,0 +1,64 @@ +type float-stack { + data: (handle array float) + top: int +} + +fn initialize-float-stack _self: (addr float-stack), n: int { + var self/esi: (addr float-stack) <- copy _self + var d/edi: (addr handle array float) <- get self, data + populate d, n + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn clear-float-stack _self: (addr float-stack) { + var self/esi: (addr float-stack) <- copy _self + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn push-float-stack _self: (addr float-stack), _val: float { + var self/esi: (addr float-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array float) <- get self, data + var data/eax: (addr array float) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-addr/edx: (addr float) <- index data, top + var val/xmm0: float <- copy _val + copy-to *dest-addr, val + add-to *top-addr, 1 +} + +fn pop-float-stack _self: (addr float-stack) -> _/xmm0: float { + var self/esi: (addr float-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + { + compare *top-addr, 0 + break-if-> + var zero: float + return zero + } + subtract-from *top-addr, 1 + var data-ah/edx: (addr handle array float) <- get self, data + var data/eax: (addr array float) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var result-addr/eax: (addr float) <- index data, top + return *result-addr +} + +fn float-stack-empty? _self: (addr float-stack) -> _/eax: boolean { + var self/esi: (addr float-stack) <- copy _self + var top-addr/eax: (addr int) <- get self, top + compare *top-addr, 0 + { + break-if-!= + return 1/true + } + return 0/false +} + +fn float-stack-length _self: (addr float-stack) -> _/eax: int { + var self/esi: (addr float-stack) <- copy _self + var top-addr/eax: (addr int) <- get self, top + return *top-addr +} diff --git a/linux/tile/gap-buffer.mu b/linux/tile/gap-buffer.mu new file mode 100644 index 00000000..0132daf0 --- /dev/null +++ b/linux/tile/gap-buffer.mu @@ -0,0 +1,343 @@ +type gap-buffer { + left: grapheme-stack + right: grapheme-stack +} + +fn initialize-gap-buffer _self: (addr gap-buffer) { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + initialize-grapheme-stack left, 0x10/max-word-size + var right/eax: (addr grapheme-stack) <- get self, right + initialize-grapheme-stack right, 0x10/max-word-size +} + +# just for tests +fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) { + initialize-gap-buffer self + var stream-storage: (stream byte 0x10/max-word-size) + var stream/ecx: (addr stream byte) <- address stream-storage + write stream, s + { + var done?/eax: boolean <- stream-empty? stream + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme stream + add-grapheme-at-gap self, g + loop + } +} + +fn gap-buffer-to-string self: (addr gap-buffer), out: (addr handle array byte) { + var s-storage: (stream byte 0x100) + var s/ecx: (addr stream byte) <- address s-storage + emit-gap-buffer self, s + stream-to-array s, out +} + +fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) { + var self/esi: (addr gap-buffer) <- copy _self + clear-stream out + var left/eax: (addr grapheme-stack) <- get self, left + emit-stack-from-bottom left, out + var right/eax: (addr grapheme-stack) <- get self, right + emit-stack-from-top right, out +} + +# dump stack from bottom to top +fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + write-grapheme out, *g + i <- increment + loop + } +} + +# dump stack from top to bottom +fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + var g/edx: (addr grapheme) <- index data, i + write-grapheme out, *g + i <- decrement + loop + } +} + +fn render-gap-buffer screen: (addr screen), _gap: (addr gap-buffer) { + var gap/esi: (addr gap-buffer) <- copy _gap + var left/eax: (addr grapheme-stack) <- get gap, left + render-stack-from-bottom left, screen + var right/eax: (addr grapheme-stack) <- get gap, right + render-stack-from-top right, screen +} + +fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int { + var gap/esi: (addr gap-buffer) <- copy _gap + var left/eax: (addr grapheme-stack) <- get gap, left + var tmp/eax: (addr int) <- get left, top + var left-length/ecx: int <- copy *tmp + var right/esi: (addr grapheme-stack) <- get gap, right + tmp <- get right, top + var result/eax: int <- copy *tmp + result <- add left-length + return result +} + +fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + push-grapheme-stack left, g +} + +fn gap-to-start self: (addr gap-buffer) { + { + var curr/eax: grapheme <- gap-left self + compare curr, -1 + loop-if-!= + } +} + +fn gap-to-end self: (addr gap-buffer) { + { + var curr/eax: grapheme <- gap-right self + compare curr, -1 + loop-if-!= + } +} + +fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + var result/eax: boolean <- grapheme-stack-empty? left + return result +} + +fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + var right/eax: (addr grapheme-stack) <- get self, right + var result/eax: boolean <- grapheme-stack-empty? right + return result +} + +fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var g/eax: grapheme <- copy 0 + var right/ecx: (addr grapheme-stack) <- get self, right + g <- pop-grapheme-stack right + compare g, -1 + { + break-if-= + var left/ecx: (addr grapheme-stack) <- get self, left + push-grapheme-stack left, g + } + return g +} + +fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var g/eax: grapheme <- copy 0 + { + var left/ecx: (addr grapheme-stack) <- get self, left + g <- pop-grapheme-stack left + } + compare g, -1 + { + break-if-= + var right/ecx: (addr grapheme-stack) <- get self, right + push-grapheme-stack right, g + } + return g +} + +fn gap-index _self: (addr gap-buffer) -> _/eax: int { + var self/eax: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + var top-addr/eax: (addr int) <- get left, top + var result/eax: int <- copy *top-addr + return result +} + +fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + # try to read from left + var left/eax: (addr grapheme-stack) <- get self, left + var top-addr/ecx: (addr int) <- get left, top + compare *top-addr, 0 + { + break-if-<= + var data-ah/eax: (addr handle array grapheme) <- get left, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var result-addr/eax: (addr grapheme) <- index data, 0 + return *result-addr + } + # try to read from right + var right/eax: (addr grapheme-stack) <- get self, right + top-addr <- get right, top + compare *top-addr, 0 + { + break-if-<= + var data-ah/eax: (addr handle array grapheme) <- get right, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var top/ecx: int <- copy *top-addr + top <- decrement + var result-addr/eax: (addr grapheme) <- index data, top + return *result-addr + } + # give up + return -1 +} + +fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + # try to read from left + var left/ecx: (addr grapheme-stack) <- get self, left + var top-addr/edx: (addr int) <- get left, top + compare *top-addr, 0 + { + break-if-<= + var result/eax: grapheme <- pop-grapheme-stack left + push-grapheme-stack left, result + return result + } + # give up + return -1 +} + +fn delete-before-gap _self: (addr gap-buffer) { + var self/eax: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + var dummy/eax: grapheme <- pop-grapheme-stack left +} + +fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme { + var self/eax: (addr gap-buffer) <- copy _self + var right/eax: (addr grapheme-stack) <- get self, right + var result/eax: grapheme <- pop-grapheme-stack right + return result +} + +fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + # complication: graphemes may be multiple bytes + # so don't rely on length + # instead turn the expected result into a stream and arrange to read from it in order + var stream-storage: (stream byte 0x10/max-word-size) + var expected-stream/ecx: (addr stream byte) <- address stream-storage + write expected-stream, s + # compare left + var left/edx: (addr grapheme-stack) <- get self, left + var result/eax: boolean <- prefix-match? left, expected-stream + compare result, 0/false + { + break-if-!= + return result + } + # compare right + var right/edx: (addr grapheme-stack) <- get self, right + result <- suffix-match? right, expected-stream + compare result, 0/false + { + break-if-!= + return result + } + # ensure there's nothing left over + result <- stream-empty? expected-stream + return result +} + +fn test-gap-buffer-equal-from-end? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + # gap is at end (right is empty) + var _result/eax: boolean <- gap-buffer-equal? g, "aaa" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-end?" +} + +fn test-gap-buffer-equal-from-middle? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + var dummy/eax: grapheme <- gap-left g + # gap is in the middle + var _result/eax: boolean <- gap-buffer-equal? g, "aaa" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-middle?" +} + +fn test-gap-buffer-equal-from-start? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + var dummy/eax: grapheme <- gap-left g + dummy <- gap-left g + dummy <- gap-left g + # gap is at the start + var _result/eax: boolean <- gap-buffer-equal? g, "aaa" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-start?" +} + +fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) { + # obtain src-a, dest-a + var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah + var _src-a/eax: (addr gap-buffer) <- lookup *src-ah + var src-a/esi: (addr gap-buffer) <- copy _src-a + var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah + var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah + var dest-a/edi: (addr gap-buffer) <- copy _dest-a + # copy left grapheme-stack + var src/ecx: (addr grapheme-stack) <- get src-a, left + var dest/edx: (addr grapheme-stack) <- get dest-a, left + copy-grapheme-stack src, dest + # copy right grapheme-stack + src <- get src-a, right + dest <- get dest-a, right + copy-grapheme-stack src, dest +} + +fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + var curr/ecx: (addr grapheme-stack) <- get self, left + var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr + { + compare result, 0/false + break-if-= + curr <- get self, right + result <- grapheme-stack-is-decimal-integer? curr + } + return result +} diff --git a/linux/tile/grapheme-stack.mu b/linux/tile/grapheme-stack.mu new file mode 100644 index 00000000..e0d40ecc --- /dev/null +++ b/linux/tile/grapheme-stack.mu @@ -0,0 +1,191 @@ +type grapheme-stack { + data: (handle array grapheme) + top: int +} + +fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int { + var self/esi: (addr grapheme-stack) <- copy _self + var d/edi: (addr handle array grapheme) <- get self, data + populate d, n + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn clear-grapheme-stack _self: (addr grapheme-stack) { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + compare *top, 0 + { + break-if-!= + return 1/true + } + return 0/false +} + +fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme { + var self/esi: (addr grapheme-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array grapheme) <- get self, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-addr/edx: (addr grapheme) <- index data, top + var val/eax: grapheme <- copy _val + copy-to *dest-addr, val + add-to *top-addr, 1 +} + +fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: grapheme { + var self/esi: (addr grapheme-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + { + compare *top-addr, 0 + break-if-> + return -1 + } + subtract-from *top-addr, 1 + var data-ah/edx: (addr handle array grapheme) <- get self, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var result-addr/eax: (addr grapheme) <- index data, top + return *result-addr +} + +fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) { + var src/esi: (addr grapheme-stack) <- copy _src + var data-ah/edi: (addr handle array grapheme) <- get src, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get src, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + push-grapheme-stack dest, *g + i <- increment + loop + } +} + +# dump stack to screen from bottom to top +# don't move the cursor or anything +fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + print-grapheme screen, *g + i <- increment + loop + } +} + +# dump stack to screen from top to bottom +# don't move the cursor or anything +fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + var g/edx: (addr grapheme) <- index data, i + print-grapheme screen, *g + i <- decrement + loop + } +} + +# compare from bottom +# beware: modifies 'stream', which must be disposed of after a false result +fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/ebx: int <- copy 0 + { + compare i, *top-addr + break-if->= + # if curr != expected, return false + { + var curr-a/edx: (addr grapheme) <- index data, i + var expected/eax: grapheme <- read-grapheme s + { + compare expected, *curr-a + break-if-= + return 0/false + } + } + i <- increment + loop + } + return 1 # true +} + +# compare from bottom +# beware: modifies 'stream', which must be disposed of after a false result +fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/eax: (addr int) <- get self, top + var i/ebx: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + { + var curr-a/edx: (addr grapheme) <- index data, i + var expected/eax: grapheme <- read-grapheme s + # if curr != expected, return false + { + compare expected, *curr-a + break-if-= + return 0/false + } + } + i <- decrement + loop + } + return 1 # true +} + +fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/eax: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edx: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/ebx: int <- copy 0 + var result/eax: boolean <- copy 1/true + $grapheme-stack-is-integer?:loop: { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + result <- is-decimal-digit? *g + compare result, 0/false + break-if-= + i <- increment + loop + } + return result +} diff --git a/linux/tile/main.mu b/linux/tile/main.mu new file mode 100644 index 00000000..e0daaf1b --- /dev/null +++ b/linux/tile/main.mu @@ -0,0 +1,133 @@ +fn main args-on-stack: (addr array addr array byte) -> _/ebx: int { + var args/eax: (addr array addr array byte) <- copy args-on-stack + var len/ecx: int <- length args + compare len, 2 + { + break-if-!= + # if single arg is 'test', run tests + var tmp/ecx: (addr addr array byte) <- index args, 1 + var tmp2/eax: boolean <- string-equal? *tmp, "test" + compare tmp2, 0/false + { + break-if-= + run-tests + return 0 # TODO: get at Num-test-failures somehow + } + # if single arg is 'screen', run in full-screen mode + tmp2 <- string-equal? *tmp, "screen" + compare tmp2, 0/false + { + break-if-= + interactive + return 0 + } + # if single arg is 'type', run in typewriter mode + tmp2 <- string-equal? *tmp, "type" + compare tmp2, 0/false + { + break-if-= + repl + return 0 + } + # if single arg is 'test' ... + tmp2 <- string-equal? *tmp, "test2" + compare tmp2, 0/false + { + break-if-= + test + return 0 + } + } + # otherwise error message + print-string-to-real-screen "usage:\n" + print-string-to-real-screen " to run tests: tile test\n" + print-string-to-real-screen " full-screen mode: tile screen\n" + print-string-to-real-screen " regular REPL: tile type\n" + return 1 +} + +fn interactive { + enable-screen-grid-mode + enable-keyboard-immediate-mode + var env-storage: environment + var env/esi: (addr environment) <- address env-storage + initialize-environment env + { + render env + var key/eax: grapheme <- read-key-from-real-keyboard + compare key, 0x11/ctrl-q + break-if-= + process env, key + loop + } + enable-keyboard-type-mode + enable-screen-type-mode +} + +fn test { + var env-storage: environment + var env/esi: (addr environment) <- address env-storage + initialize-environment-with-fake-screen env, 0x20, 0xa0 + render env +} + +fn process-all env: (addr environment), cmds: (addr array byte) { + var cmds-stream: (stream byte 0x100) + var cmds-stream-a/esi: (addr stream byte) <- address cmds-stream + write cmds-stream-a, cmds + { + var done?/eax: boolean <- stream-empty? cmds-stream-a + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme cmds-stream-a + process env, g + loop + } +} + +fn repl { + { + # prompt + print-string-to-real-screen "> " + # read + var line-storage: (stream byte 0x100) + var line/ecx: (addr stream byte) <- address line-storage + clear-stream line + read-line-from-real-keyboard line + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + # parse + var env-storage: environment + var env/esi: (addr environment) <- address env-storage + initialize-environment env + { + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme line + process env, g + loop + } + # eval + var stack-storage: value-stack + var stack/edi: (addr value-stack) <- address stack-storage + initialize-value-stack stack, 0x10 + evaluate-environment env, stack + # print + var empty?/eax: boolean <- value-stack-empty? stack + { + compare empty?, 0/false + break-if-!= + var result/xmm0: float <- pop-number-from-value-stack stack + print-float-decimal-approximate 0, result, 3 + print-string 0, "\n" + print-string 0, "width: " + var width/eax: int <- float-size result, 3 + print-int32-decimal 0, width + print-string 0, "\n" + } + # + loop + } +} diff --git a/linux/tile/rpn.mu b/linux/tile/rpn.mu new file mode 100644 index 00000000..bf81308b --- /dev/null +++ b/linux/tile/rpn.mu @@ -0,0 +1,911 @@ +fn evaluate functions: (addr handle function), bindings: (addr table), scratch: (addr line), end: (addr word), out: (addr value-stack) { + var line/eax: (addr line) <- copy scratch + var word-ah/eax: (addr handle word) <- get line, data + var curr/eax: (addr word) <- lookup *word-ah + var curr-stream-storage: (stream byte 0x10) + var curr-stream/edi: (addr stream byte) <- address curr-stream-storage + clear-value-stack out + $evaluate:loop: { + # precondition (should never hit) + compare curr, 0 + break-if-= + # update curr-stream + emit-word curr, curr-stream +#? print-string-to-real-screen "eval: " +#? print-stream-to-real-screen curr-stream +#? print-string-to-real-screen "\n" + $evaluate:process-word: { + ### if curr-stream is an operator, perform it + ## numbers + { + var is-add?/eax: boolean <- stream-data-equal? curr-stream, "+" + compare is-add?, 0 + break-if-= + var _b/xmm0: float <- pop-number-from-value-stack out + var b/xmm1: float <- copy _b + var a/xmm0: float <- pop-number-from-value-stack out + a <- add b + push-number-to-value-stack out, a + break $evaluate:process-word + } + { + var is-sub?/eax: boolean <- stream-data-equal? curr-stream, "-" + compare is-sub?, 0 + break-if-= + var _b/xmm0: float <- pop-number-from-value-stack out + var b/xmm1: float <- copy _b + var a/xmm0: float <- pop-number-from-value-stack out + a <- subtract b + push-number-to-value-stack out, a + break $evaluate:process-word + } + { + var is-mul?/eax: boolean <- stream-data-equal? curr-stream, "*" + compare is-mul?, 0 + break-if-= + var _b/xmm0: float <- pop-number-from-value-stack out + var b/xmm1: float <- copy _b + var a/xmm0: float <- pop-number-from-value-stack out + a <- multiply b + push-number-to-value-stack out, a + break $evaluate:process-word + } + { + var is-div?/eax: boolean <- stream-data-equal? curr-stream, "/" + compare is-div?, 0 + break-if-= + var _b/xmm0: float <- pop-number-from-value-stack out + var b/xmm1: float <- copy _b + var a/xmm0: float <- pop-number-from-value-stack out + a <- divide b + push-number-to-value-stack out, a + break $evaluate:process-word + } + { + var is-sqrt?/eax: boolean <- stream-data-equal? curr-stream, "sqrt" + compare is-sqrt?, 0 + break-if-= + var a/xmm0: float <- pop-number-from-value-stack out + a <- square-root a + push-number-to-value-stack out, a + break $evaluate:process-word + } + ## strings/arrays + { + var is-len?/eax: boolean <- stream-data-equal? curr-stream, "len" + compare is-len?, 0 + break-if-= +#? print-string 0, "is len\n" + # pop target-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= +#? print-string 0, "stack has stuff\n" + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # check target-val is a string or array + var target-type-addr/eax: (addr int) <- get target-val, type + compare *target-type-addr, 1/string + { + break-if-!= + # compute length + var src-ah/eax: (addr handle array byte) <- get target-val, text-data + var src/eax: (addr array byte) <- lookup *src-ah + var result/ebx: int <- length src + var result-f/xmm0: float <- convert result + # save result into target-val + var type-addr/eax: (addr int) <- get target-val, type + copy-to *type-addr, 0/int + var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data + clear-object target-string-ah + var target/eax: (addr float) <- get target-val, number-data + copy-to *target, result-f + break $evaluate:process-word + } + compare *target-type-addr, 2/array + { + break-if-!= + # compute length + var src-ah/eax: (addr handle array value) <- get target-val, array-data + var src/eax: (addr array value) <- lookup *src-ah + var result/ebx: int <- length src + var result-f/xmm0: float <- convert result + # save result into target-val + var type-addr/eax: (addr int) <- get target-val, type + copy-to *type-addr, 0/int + var target-array-ah/eax: (addr handle array value) <- get target-val, array-data + clear-object target-array-ah + var target/eax: (addr float) <- get target-val, number-data + copy-to *target, result-f + break $evaluate:process-word + } + } + ## files + { + var is-open?/eax: boolean <- stream-data-equal? curr-stream, "open" + compare is-open?, 0 + break-if-= + # pop target-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # check target-val is a string + var target-type-addr/eax: (addr int) <- get target-val, type + compare *target-type-addr, 1/string + break-if-!= + # open target-val as a filename and save the handle in target-val + var src-ah/eax: (addr handle array byte) <- get target-val, text-data + var src/eax: (addr array byte) <- lookup *src-ah + var result-ah/ecx: (addr handle buffered-file) <- get target-val, file-data + open src, 0, result-ah # write? = false + # save result into target-val + var type-addr/eax: (addr int) <- get target-val, type + copy-to *type-addr, 3/file + var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data + var filename-ah/ecx: (addr handle array byte) <- get target-val, filename + copy-object target-string-ah, filename-ah + clear-object target-string-ah + break $evaluate:process-word + } + { + var is-read?/eax: boolean <- stream-data-equal? curr-stream, "read" + compare is-read?, 0 + break-if-= + # pop target-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # check target-val is a file + var target-type-addr/eax: (addr int) <- get target-val, type + compare *target-type-addr, 3/file + break-if-!= + # read a line from the file and save in target-val + # read target-val as a filename and save the handle in target-val + var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data + var file/eax: (addr buffered-file) <- lookup *file-ah + var s: (stream byte 0x100) + var s-addr/ecx: (addr stream byte) <- address s + read-line-buffered file, s-addr + var target/eax: (addr handle array byte) <- get target-val, text-data + stream-to-array s-addr, target + # save result into target-val + var type-addr/eax: (addr int) <- get target-val, type + copy-to *type-addr, 1/string + var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data + clear-object target-file-ah + break $evaluate:process-word + } + { + var is-slurp?/eax: boolean <- stream-data-equal? curr-stream, "slurp" + compare is-slurp?, 0 + break-if-= + # pop target-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # check target-val is a file + var target-type-addr/eax: (addr int) <- get target-val, type + compare *target-type-addr, 3/file + break-if-!= + # slurp all contents from file and save in target-val + # read target-val as a filename and save the handle in target-val + var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data + var file/eax: (addr buffered-file) <- lookup *file-ah + var s: (stream byte 0x100) + var s-addr/ecx: (addr stream byte) <- address s + slurp file, s-addr + var target/eax: (addr handle array byte) <- get target-val, text-data + stream-to-array s-addr, target + # save result into target-val + var type-addr/eax: (addr int) <- get target-val, type + copy-to *type-addr, 1/string + var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data + clear-object target-file-ah + break $evaluate:process-word + } + { + var is-lines?/eax: boolean <- stream-data-equal? curr-stream, "lines" + compare is-lines?, 0 + break-if-= + # pop target-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # check target-val is a file + var target-type-addr/eax: (addr int) <- get target-val, type + compare *target-type-addr, 3/file + break-if-!= + # read all lines from file and save as an array of strings in target-val + # read target-val as a filename and save the handle in target-val + var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data + var file/eax: (addr buffered-file) <- lookup *file-ah + var s: (stream byte 0x100) + var s-addr/ecx: (addr stream byte) <- address s + slurp file, s-addr + var tmp-ah/eax: (addr handle array byte) <- get target-val, text-data + stream-to-array s-addr, tmp-ah + var tmp/eax: (addr array byte) <- lookup *tmp-ah +#? enable-screen-type-mode +#? print-string 0, tmp + var h: (handle array (handle array byte)) + { + var ah/edx: (addr handle array (handle array byte)) <- address h + split-string tmp, 0xa, ah + } + var target/eax: (addr handle array value) <- get target-val, array-data + save-lines h, target + # save result into target-val + var type-addr/eax: (addr int) <- get target-val, type + copy-to *type-addr, 2/array + var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data + var empty-file: (handle buffered-file) + copy-handle empty-file, target-file-ah + var target-text-ah/eax: (addr handle array byte) <- get target-val, text-data + var empty-text: (handle array byte) + copy-handle empty-text, target-text-ah + break $evaluate:process-word + } + ## screens + { + var is-fake-screen?/eax: boolean <- stream-data-equal? curr-stream, "fake-screen" + compare is-fake-screen?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + # pop width and height from out + var nrows-f/xmm0: float <- pop-number-from-value-stack out2 + var nrows/edx: int <- convert nrows-f + var ncols-f/xmm0: float <- pop-number-from-value-stack out2 + var ncols/ebx: int <- convert ncols-f + # define a new screen with those dimensions + var screen-h: (handle screen) + var screen-ah/eax: (addr handle screen) <- address screen-h + allocate screen-ah + var screen/eax: (addr screen) <- lookup screen-h + initialize-screen screen, nrows, ncols + # push screen to stack + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + increment *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + var type/eax: (addr int) <- get target-val, type + copy-to *type, 4/screen + var dest/eax: (addr handle screen) <- get target-val, screen-data + copy-handle screen-h, dest + break $evaluate:process-word + } + { + var is-print?/eax: boolean <- stream-data-equal? curr-stream, "print" + compare is-print?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + # pop string from out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + decrement *top-addr + var data-ah/eax: (addr handle array value) <- get out2, data + var _data/eax: (addr array value) <- lookup *data-ah + var data/edi: (addr array value) <- copy _data + var top/eax: int <- copy *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var s/esi: (addr value) <- index data, dest-offset + # select target screen from top of out (but don't pop it) + compare *top-addr, 0 + break-if-<= + var top/eax: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + var type/eax: (addr int) <- get target-val, type + compare *type, 4/screen + break-if-!= + # print string to target screen + var dest-ah/eax: (addr handle screen) <- get target-val, screen-data + var dest/eax: (addr screen) <- lookup *dest-ah + var r/ecx: (addr int) <- get dest, cursor-row + var c/edx: (addr int) <- get dest, cursor-col + render-value-at dest, *r, *c, s, 0 + break $evaluate:process-word + } + { + var is-move?/eax: boolean <- stream-data-equal? curr-stream, "move" + compare is-move?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + # pop args + var r-f/xmm0: float <- pop-number-from-value-stack out2 + var r/ecx: int <- convert r-f + var c-f/xmm0: float <- pop-number-from-value-stack out2 + var c/edx: int <- convert c-f + # select screen from top of out (but don't pop it) + var top-addr/ebx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var _data/eax: (addr array value) <- lookup *data-ah + var data/edi: (addr array value) <- copy _data + var top/eax: int <- copy *top-addr + top <- decrement + var target-offset/eax: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, target-offset + var type/eax: (addr int) <- get target-val, type + compare *type, 4/screen + break-if-!= + var target-ah/eax: (addr handle screen) <- get target-val, screen-data + var target/eax: (addr screen) <- lookup *target-ah + move-cursor target, r, c + break $evaluate:process-word + } + { + var is-up?/eax: boolean <- stream-data-equal? curr-stream, "up" + compare is-up?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + var top-addr/ebx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + # pop args + var d-f/xmm0: float <- pop-number-from-value-stack out2 + var d/ecx: int <- convert d-f + # select screen from top of out (but don't pop it) + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var _data/eax: (addr array value) <- lookup *data-ah + var data/edi: (addr array value) <- copy _data + var top/eax: int <- copy *top-addr + top <- decrement + var target-offset/eax: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, target-offset + var type/eax: (addr int) <- get target-val, type + compare *type, 4/screen + break-if-!= + var target-ah/eax: (addr handle screen) <- get target-val, screen-data + var _target/eax: (addr screen) <- lookup *target-ah + var target/edi: (addr screen) <- copy _target + var r/edx: (addr int) <- get target, cursor-row + var c/eax: (addr int) <- get target, cursor-col + var col/eax: int <- copy *c + { + compare d, 0 + break-if-<= + compare *r, 1 + break-if-<= + print-string target "│" + decrement *r + move-cursor target, *r, col + d <- decrement + loop + } + break $evaluate:process-word + } + { + var is-down?/eax: boolean <- stream-data-equal? curr-stream, "down" + compare is-down?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + var top-addr/ebx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + # pop args + var d-f/xmm0: float <- pop-number-from-value-stack out2 + var d/ecx: int <- convert d-f + # select screen from top of out (but don't pop it) + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var _data/eax: (addr array value) <- lookup *data-ah + var data/edi: (addr array value) <- copy _data + var top/eax: int <- copy *top-addr + top <- decrement + var target-offset/eax: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, target-offset + var type/eax: (addr int) <- get target-val, type + compare *type, 4/screen + break-if-!= + var target-ah/eax: (addr handle screen) <- get target-val, screen-data + var _target/eax: (addr screen) <- lookup *target-ah + var target/edi: (addr screen) <- copy _target + var bound-a/ebx: (addr int) <- get target, num-rows + var bound/ebx: int <- copy *bound-a + var r/edx: (addr int) <- get target, cursor-row + var c/eax: (addr int) <- get target, cursor-col + var col/eax: int <- copy *c + { + compare d, 0 + break-if-<= + compare *r, bound + break-if->= + print-string target "│" + increment *r + move-cursor target, *r, col + d <- decrement + loop + } + break $evaluate:process-word + } + { + var is-left?/eax: boolean <- stream-data-equal? curr-stream, "left" + compare is-left?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + var top-addr/ebx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + # pop args + var d-f/xmm0: float <- pop-number-from-value-stack out2 + var d/ecx: int <- convert d-f + # select screen from top of out (but don't pop it) + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var _data/eax: (addr array value) <- lookup *data-ah + var data/edi: (addr array value) <- copy _data + var top/eax: int <- copy *top-addr + top <- decrement + var target-offset/eax: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, target-offset + var type/eax: (addr int) <- get target-val, type + compare *type, 4/screen + break-if-!= + var target-ah/eax: (addr handle screen) <- get target-val, screen-data + var _target/eax: (addr screen) <- lookup *target-ah + var target/edi: (addr screen) <- copy _target + var c/edx: (addr int) <- get target, cursor-col + var r/eax: (addr int) <- get target, cursor-row + var row/eax: int <- copy *r + { + compare d, 0 + break-if-<= + compare *c, 1 + break-if-<= + print-string target "─" + decrement *c + decrement *c # second one to undo the print above + move-cursor target, row, *c + d <- decrement + loop + } + break $evaluate:process-word + } + { + var is-right?/eax: boolean <- stream-data-equal? curr-stream, "right" + compare is-right?, 0 + break-if-= + var out2/esi: (addr value-stack) <- copy out + var top-addr/ebx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + # pop args + var _d/xmm0: float <- pop-number-from-value-stack out2 + var d/ecx: int <- convert _d + # select screen from top of out (but don't pop it) + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var _data/eax: (addr array value) <- lookup *data-ah + var data/edi: (addr array value) <- copy _data + var top/eax: int <- copy *top-addr + top <- decrement + var target-offset/eax: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, target-offset + var type/eax: (addr int) <- get target-val, type + compare *type, 4/screen + break-if-!= + var target-ah/eax: (addr handle screen) <- get target-val, screen-data + var _target/eax: (addr screen) <- lookup *target-ah + var target/edi: (addr screen) <- copy _target + var bound-a/ebx: (addr int) <- get target, num-rows + var bound/ebx: int <- copy *bound-a + var c/edx: (addr int) <- get target, cursor-col + var r/eax: (addr int) <- get target, cursor-row + var row/eax: int <- copy *r + { + compare d, 0 + break-if-<= + compare *c, bound + break-if->= + print-string target "─" + # no increment; the print took care of it + move-cursor target, row, *c + d <- decrement + loop + } + break $evaluate:process-word + } + ## HACKS: we're trying to avoid turning this into Forth + { + var is-dup?/eax: boolean <- stream-data-equal? curr-stream, "dup" + compare is-dup?, 0 + break-if-= + # read src-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/ecx: int <- copy *top-addr + top <- decrement + var offset/edx: (offset value) <- compute-offset data, top + var src-val/edx: (addr value) <- index data, offset + # push a copy of it + top <- increment + var offset/ebx: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, offset + copy-object src-val, target-val + # commit + var top-addr/ecx: (addr int) <- get out2, top + increment *top-addr + break $evaluate:process-word + } + { + var is-swap?/eax: boolean <- stream-data-equal? curr-stream, "swap" + compare is-swap?, 0 + break-if-= + # read top-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/ecx: int <- copy *top-addr + top <- decrement + var offset/edx: (offset value) <- compute-offset data, top + var top-val/edx: (addr value) <- index data, offset + # read next val from out + top <- decrement + var offset/ebx: (offset value) <- compute-offset data, top + var pen-top-val/ebx: (addr value) <- index data, offset + # swap + var tmp: value + var tmp-a/eax: (addr value) <- address tmp + copy-object top-val, tmp-a + copy-object pen-top-val, top-val + copy-object tmp-a, pen-top-val + break $evaluate:process-word + } + ### if curr-stream defines a binding, save top of stack to bindings + { + var done?/eax: boolean <- stream-empty? curr-stream + compare done?, 0/false + break-if-!= + var new-byte/eax: byte <- read-byte curr-stream + compare new-byte, 0x3d/= + break-if-!= + # pop target-val from out + var out2/esi: (addr value-stack) <- copy out + var top-addr/ecx: (addr int) <- get out2, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get out2, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # create binding from curr-stream to target-val + var key-h: (handle array byte) + var key/ecx: (addr handle array byte) <- address key-h + stream-to-array curr-stream, key + bind-in-table bindings, key, target-val + break $evaluate:process-word + } + rewind-stream curr-stream + ### if curr-stream is a known function name, call it appropriately + { + var callee-h: (handle function) + var callee-ah/eax: (addr handle function) <- address callee-h + find-function functions, curr-stream, callee-ah + var callee/eax: (addr function) <- lookup *callee-ah + compare callee, 0 + break-if-= + perform-call callee, out, functions + break $evaluate:process-word + } + ### if it's a name, push its value + { + compare bindings, 0 + break-if-= + var tmp: (handle array byte) + var curr-string-ah/edx: (addr handle array byte) <- address tmp + stream-to-array curr-stream, curr-string-ah # unfortunate leak + var curr-string/eax: (addr array byte) <- lookup *curr-string-ah + var val-storage: (handle value) + var val-ah/edi: (addr handle value) <- address val-storage + lookup-binding bindings, curr-string, val-ah + var val/eax: (addr value) <- lookup *val-ah + compare val, 0 + break-if-= + push-value-stack out, val + break $evaluate:process-word + } + ### if the word starts with a quote and ends with a quote, turn it into a string + { + var start/eax: byte <- stream-first curr-stream + compare start, 0x22/double-quote + break-if-!= + var end/eax: byte <- stream-final curr-stream + compare end, 0x22/double-quote + break-if-!= + var h: (handle array byte) + var s/eax: (addr handle array byte) <- address h + unquote-stream-to-array curr-stream, s # leak + push-string-to-value-stack out, *s + break $evaluate:process-word + } + ### if the word starts with a '[' and ends with a ']', turn it into an array + { + var start/eax: byte <- stream-first curr-stream + compare start, 0x5b/[ + break-if-!= + var end/eax: byte <- stream-final curr-stream + compare end, 0x5d/] + break-if-!= + # wastefully create a new input string to strip quotes + var h: (handle array value) + var input-ah/eax: (addr handle array byte) <- address h + unquote-stream-to-array curr-stream, input-ah # leak + # wastefully parse input into int-array + # TODO: support parsing arrays of other types + var input/eax: (addr array byte) <- lookup *input-ah + var h2: (handle array int) + var int-array-ah/esi: (addr handle array int) <- address h2 + parse-array-of-decimal-ints input, int-array-ah # leak + var _int-array/eax: (addr array int) <- lookup *int-array-ah + var int-array/esi: (addr array int) <- copy _int-array + var len/ebx: int <- length int-array + # push value-array of same size as int-array + var h3: (handle array value) + var value-array-ah/eax: (addr handle array value) <- address h3 + populate value-array-ah, len + push-array-to-value-stack out, *value-array-ah + # copy int-array into value-array + var _value-array/eax: (addr array value) <- lookup *value-array-ah + var value-array/edi: (addr array value) <- copy _value-array + var i/eax: int <- copy 0 + { + compare i, len + break-if->= + var src-addr/ecx: (addr int) <- index int-array, i + var src/ecx: int <- copy *src-addr + var src-f/xmm0: float <- convert src + var dest-offset/edx: (offset value) <- compute-offset value-array, i + var dest-val/edx: (addr value) <- index value-array, dest-offset + var dest/edx: (addr float) <- get dest-val, number-data + copy-to *dest, src-f + i <- increment + loop + } + break $evaluate:process-word + } + ### otherwise assume it's a literal number and push it + { + var n/eax: int <- parse-decimal-int-from-stream curr-stream + var n-f/xmm0: float <- convert n + push-number-to-value-stack out, n-f + } + } + # termination check + compare curr, end + break-if-= + # update + var next-word-ah/edx: (addr handle word) <- get curr, next + curr <- lookup *next-word-ah + # + loop + } + # process next line if necessary + var line/eax: (addr line) <- copy scratch + var next-line-ah/eax: (addr handle line) <- get line, next + var next-line/eax: (addr line) <- lookup *next-line-ah + compare next-line, 0 + break-if-= + evaluate functions, bindings, next-line, end, out +} + +fn test-evaluate { + var line-storage: line + var line/esi: (addr line) <- address line-storage + var first-word-ah/eax: (addr handle word) <- get line-storage, data + allocate-word-with first-word-ah, "3" + append-word-with *first-word-ah, "=a" + var next-line-ah/eax: (addr handle line) <- get line-storage, next + allocate next-line-ah + var next-line/eax: (addr line) <- lookup *next-line-ah + var first-word-ah/eax: (addr handle word) <- get next-line, data + allocate-word-with first-word-ah, "a" + var functions-storage: (handle function) + var functions/ecx: (addr handle function) <- address functions-storage + var table-storage: table + var table/ebx: (addr table) <- address table-storage + initialize-table table, 0x10 + var stack-storage: value-stack + var stack/edi: (addr value-stack) <- address stack-storage + initialize-value-stack stack, 0x10 + evaluate functions, table, line, 0, stack + var x-f/xmm0: float <- pop-number-from-value-stack stack + var x/eax: int <- convert x-f + check-ints-equal x, 3, "F - test-evaluate" +} + +fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) { + var curr/esi: (addr handle function) <- copy first + $find-function:loop: { + var _f/eax: (addr function) <- lookup *curr + var f/ecx: (addr function) <- copy _f + compare f, 0 + break-if-= + var curr-name-ah/eax: (addr handle array byte) <- get f, name + var curr-name/eax: (addr array byte) <- lookup *curr-name-ah + var done?/eax: boolean <- stream-data-equal? name, curr-name + compare done?, 0/false + { + break-if-= + copy-handle *curr, out + break $find-function:loop + } + curr <- get f, next + loop + } +} + +fn perform-call _callee: (addr function), caller-stack: (addr value-stack), functions: (addr handle function) { + var callee/ecx: (addr function) <- copy _callee + # create bindings for args + var table-storage: table + var table/esi: (addr table) <- address table-storage + initialize-table table, 0x10 + bind-args callee, caller-stack, table + # obtain body + var body-ah/eax: (addr handle line) <- get callee, body + var body/eax: (addr line) <- lookup *body-ah + # perform call + var stack-storage: value-stack + var stack/edi: (addr value-stack) <- address stack-storage + initialize-value-stack stack, 0x10 +#? print-string-to-real-screen "about to enter recursive eval\n" + evaluate functions, table, body, 0, stack +#? print-string-to-real-screen "exited recursive eval\n" + # pop target-val from out + var top-addr/ecx: (addr int) <- get stack, top + compare *top-addr, 0 + break-if-<= + var data-ah/eax: (addr handle array value) <- get stack, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + top <- decrement + var dest-offset/edx: (offset value) <- compute-offset data, top + var target-val/edx: (addr value) <- index data, dest-offset + # stitch target-val into caller-stack + push-value-stack caller-stack, target-val +} + +# pop args from the caller-stack and bind them to successive args +# implies: function args are stored in reverse order +fn bind-args _callee: (addr function), _caller-stack: (addr value-stack), table: (addr table) { + var callee/ecx: (addr function) <- copy _callee + var curr-arg-ah/eax: (addr handle word) <- get callee, args + var curr-arg/eax: (addr word) <- lookup *curr-arg-ah + # + var curr-key-storage: (handle array byte) + var curr-key/edx: (addr handle array byte) <- address curr-key-storage + { + compare curr-arg, 0 + break-if-= + # create binding + word-to-string curr-arg, curr-key + { + # pop target-val from caller-stack + var caller-stack/esi: (addr value-stack) <- copy _caller-stack + var top-addr/ecx: (addr int) <- get caller-stack, top + compare *top-addr, 0 + break-if-<= + decrement *top-addr + var data-ah/eax: (addr handle array value) <- get caller-stack, data + var data/eax: (addr array value) <- lookup *data-ah + var top/ebx: int <- copy *top-addr + var dest-offset/ebx: (offset value) <- compute-offset data, top + var target-val/ebx: (addr value) <- index data, dest-offset + # create binding from curr-key to target-val + bind-in-table table, curr-key, target-val + } + # + var next-arg-ah/edx: (addr handle word) <- get curr-arg, next + curr-arg <- lookup *next-arg-ah + loop + } +} + +# Copy of 'simplify' that just tracks the maximum stack depth needed +# Doesn't actually need to simulate the stack, since every word has a predictable effect. +fn max-stack-depth first-word: (addr word), final-word: (addr word) -> _/edi: int { + var curr-word/eax: (addr word) <- copy first-word + var curr-depth/ecx: int <- copy 0 + var result/edi: int <- copy 0 + $max-stack-depth:loop: { + $max-stack-depth:process-word: { + # handle operators + { + var is-add?/eax: boolean <- word-equal? curr-word, "+" + compare is-add?, 0 + break-if-= + curr-depth <- decrement + break $max-stack-depth:process-word + } + { + var is-sub?/eax: boolean <- word-equal? curr-word, "-" + compare is-sub?, 0 + break-if-= + curr-depth <- decrement + break $max-stack-depth:process-word + } + { + var is-mul?/eax: boolean <- word-equal? curr-word, "*" + compare is-mul?, 0 + break-if-= + curr-depth <- decrement + break $max-stack-depth:process-word + } + # otherwise it's an int (do we need error-checking?) + curr-depth <- increment + # update max depth if necessary + { + compare curr-depth, result + break-if-<= + result <- copy curr-depth + } + } + # if curr-word == final-word break + compare curr-word, final-word + break-if-= + # curr-word = curr-word->next + var next-word-ah/edx: (addr handle word) <- get curr-word, next + curr-word <- lookup *next-word-ah + # + loop + } + return result +} diff --git a/linux/tile/surface.mu b/linux/tile/surface.mu new file mode 100644 index 00000000..2e353022 --- /dev/null +++ b/linux/tile/surface.mu @@ -0,0 +1,412 @@ +# A surface is a large 2-D grid that you can only see a subset of through the +# screen. +# Imagine a pin going through both surface and screen. As we update the +# surface contents, the pinned point stays fixed, providing a sense of +# stability. + +type surface { + screen: (handle screen) + data: (handle array screen-cell) + nrows: int + ncols: int + screen-nrows: int + screen-ncols: int + pin-row: int # 1-indexed + pin-col: int # 1-indexed + pin-screen-row: int # 1-indexed + pin-screen-col: int # 1-indexed +} + +# intended mostly for tests; could be slow +fn initialize-surface-with _self: (addr surface), in: (addr array byte) { + var self/esi: (addr surface) <- copy _self + # fill in nrows, ncols + var nrows/ecx: int <- num-lines in + var dest/eax: (addr int) <- get self, nrows + copy-to *dest, nrows + var ncols/edx: int <- first-line-length in # assume all lines are the same length + dest <- get self, ncols + copy-to *dest, ncols + # fill in data + var len/ecx: int <- copy nrows + len <- multiply ncols + var out/edi: (addr surface) <- copy _self + var data/eax: (addr handle array screen-cell) <- get out, data + populate data, len + var data-addr/eax: (addr array screen-cell) <- lookup *data + fill-in data-addr, in + # fill in screen-nrows, screen-ncols + { + var screen-ah/eax: (addr handle screen) <- get self, screen + var _screen-addr/eax: (addr screen) <- lookup *screen-ah + var screen-addr/edi: (addr screen) <- copy _screen-addr + var nrows/eax: int <- copy 0 + var ncols/ecx: int <- copy 0 + nrows, ncols <- screen-size screen-addr + var dest/edi: (addr int) <- get self, screen-nrows + copy-to *dest, nrows + dest <- get self, screen-ncols + copy-to *dest, ncols + } +} + +fn pin-surface-at _self: (addr surface), r: int, c: int { + var self/esi: (addr surface) <- copy _self + var dest/ecx: (addr int) <- get self, pin-row + var tmp/eax: int <- copy r + copy-to *dest, tmp + dest <- get self, pin-col + tmp <- copy c + copy-to *dest, tmp +} + +fn pin-surface-to _self: (addr surface), sr: int, sc: int { + var self/esi: (addr surface) <- copy _self + var dest/ecx: (addr int) <- get self, pin-screen-row + var tmp/eax: int <- copy sr + copy-to *dest, tmp + dest <- get self, pin-screen-col + tmp <- copy sc + copy-to *dest, tmp +} + +fn render-surface _self: (addr surface) { +#? print-string-to-real-screen "render-surface\n" + var self/esi: (addr surface) <- copy _self + # clear screen + var screen-ah/eax: (addr handle screen) <- get self, screen + var screen/eax: (addr screen) <- lookup *screen-ah + clear-screen screen + # + var nrows/edx: (addr int) <- get self, screen-nrows + var ncols/ebx: (addr int) <- get self, screen-ncols + var screen-row/ecx: int <- copy 1 + { + compare screen-row, *nrows + break-if-> + var screen-col/eax: int <- copy 1 + { + compare screen-col, *ncols + break-if-> +#? print-string-to-real-screen "X" + print-surface-cell-at self, screen-row, screen-col + screen-col <- increment + loop + } +#? print-string-to-real-screen "\n" + screen-row <- increment + loop + } +} + +fn print-surface-cell-at _self: (addr surface), screen-row: int, screen-col: int { + var self/esi: (addr surface) <- copy _self + var row/ecx: int <- screen-row-to-surface self, screen-row + var col/edx: int <- screen-col-to-surface self, screen-col + var data-ah/edi: (addr handle array screen-cell) <- get self, data + var _data-addr/eax: (addr array screen-cell) <- lookup *data-ah + var data-addr/edi: (addr array screen-cell) <- copy _data-addr + var idx/eax: int <- surface-screen-cell-index self, row, col + # if out of bounds, print ' ' + compare idx, 0 + { + break-if->= + var space/ecx: grapheme <- copy 0x20 + var screen-ah/edi: (addr handle screen) <- get self, screen + var screen/eax: (addr screen) <- lookup *screen-ah + print-grapheme screen, space + return + } + # otherwise print the appropriate screen-cell + var offset/ecx: (offset screen-cell) <- compute-offset data-addr, idx + var src/ecx: (addr screen-cell) <- index data-addr, offset + var screen-ah/edi: (addr handle screen) <- get self, screen + var screen/eax: (addr screen) <- lookup *screen-ah + print-screen-cell screen, src +} + +# print a cell with all its formatting at the cursor location +fn print-screen-cell screen: (addr screen), _cell: (addr screen-cell) { + var cell/esi: (addr screen-cell) <- copy _cell + reset-formatting screen + var fg/eax: (addr int) <- get cell, color + var bg/ecx: (addr int) <- get cell, background-color + start-color screen, *fg, *bg + var tmp/eax: (addr boolean) <- get cell, bold? + { + compare *tmp, 0 + break-if-= + start-bold screen + } + { + tmp <- get cell, underline? + compare *tmp, 0 + break-if-= + start-underline screen + } + { + tmp <- get cell, reverse? + compare *tmp, 0 + break-if-= + start-reverse-video screen + } + { + tmp <- get cell, blink? + compare *tmp, 0 + break-if-= + start-blinking screen + } + var g/eax: (addr grapheme) <- get cell, data + print-grapheme screen, *g +#? var g2/eax: grapheme <- copy *g +#? var g3/eax: int <- copy g2 +#? print-int32-hex-to-real-screen g3 +#? print-string-to-real-screen "\n" +} + +fn surface-screen-cell-index _self: (addr surface), row: int, col: int -> _/eax: int { + var self/esi: (addr surface) <- copy _self +#? print-int32-hex-to-real-screen row +#? print-string-to-real-screen ", " +#? print-int32-hex-to-real-screen col +#? print-string-to-real-screen "\n" + var result/eax: int <- copy -1 + { + compare row, 1 + break-if-< + compare col, 1 + break-if-< + var nrows-addr/ecx: (addr int) <- get self, nrows + var nrows/ecx: int <- copy *nrows-addr + compare row, nrows + break-if-> + var ncols-addr/ecx: (addr int) <- get self, ncols + var ncols/ecx: int <- copy *ncols-addr + compare col, ncols + break-if-> + #? print-string-to-real-screen "!\n" + result <- copy row + result <- subtract 1 + result <- multiply ncols + result <- add col + result <- subtract 1 + } + return result +} + +fn screen-row-to-surface _self: (addr surface), screen-row: int -> _/ecx: int { + var self/esi: (addr surface) <- copy _self + var result/ecx: int <- copy screen-row + var tmp/eax: (addr int) <- get self, pin-row + result <- add *tmp + tmp <- get self, pin-screen-row + result <- subtract *tmp + return result +} + +fn max _a: int, b: int -> _/eax: int { + var a/eax: int <- copy _a + compare a, b + { + break-if-> + return b + } + return a +} + +fn min _a: int, b: int -> _/eax: int { + var a/eax: int <- copy _a + compare a, b + { + break-if-> + return a + } + return b +} + +fn screen-col-to-surface _self: (addr surface), screen-col: int -> _/edx: int { + var self/esi: (addr surface) <- copy _self + var result/edx: int <- copy screen-col + var tmp/eax: (addr int) <- get self, pin-col + result <- add *tmp + tmp <- get self, pin-screen-col + result <- subtract *tmp + return result +} + +fn surface-row-to-screen _self: (addr surface), row: int -> _/ecx: int { + var self/esi: (addr surface) <- copy _self + var result/ecx: int <- copy row + var tmp/eax: (addr int) <- get self, pin-screen-row + result <- add *tmp + tmp <- get self, pin-row + result <- subtract *tmp + return result +} + +fn surface-col-to-screen _self: (addr surface), col: int -> _/edx: int { + var self/esi: (addr surface) <- copy _self + var result/edx: int <- copy col + var tmp/eax: (addr int) <- get self, pin-screen-col + result <- add *tmp + tmp <- get self, pin-col + result <- subtract *tmp + return result +} + +# assumes last line doesn't end in '\n' +fn num-lines in: (addr array byte) -> _/ecx: int { + var s: (stream byte 0x100) + var s-addr/esi: (addr stream byte) <- address s + write s-addr, in + var result/ecx: int <- copy 1 + { + var done?/eax: boolean <- stream-empty? s-addr + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme s-addr + compare g, 0xa/newline + loop-if-!= + result <- increment + loop + } + return result +} + +fn first-line-length in: (addr array byte) -> _/edx: int { + var s: (stream byte 0x100) + var s-addr/esi: (addr stream byte) <- address s + write s-addr, in + var result/edx: int <- copy 0 + { + var done?/eax: boolean <- stream-empty? s-addr + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme s-addr + compare g, 0xa/newline + break-if-= + result <- increment + loop + } + return result +} + +fn fill-in _out: (addr array screen-cell), in: (addr array byte) { + var s: (stream byte 0x100) + var out/edi: (addr array screen-cell) <- copy _out + var s-addr/esi: (addr stream byte) <- address s + write s-addr, in + var idx/ecx: int <- copy 0 + { + var done?/eax: boolean <- stream-empty? s-addr + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme s-addr + compare g, 0xa/newline + loop-if-= + var offset/edx: (offset screen-cell) <- compute-offset out, idx + var dest/edx: (addr screen-cell) <- index out, offset + var dest2/edx: (addr grapheme) <- get dest, data + copy-to *dest2, g + idx <- increment + loop + } +} + +# pin (1, 1) to (1, 1) on screen +fn test-surface-pin-at-origin { + var s: surface + var s-addr/esi: (addr surface) <- address s + # surface contents are a fixed grid with 8 rows and 6 columns + # (strip vowels second time around to break vertical alignment of letters) + initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz" + pin-surface-at s-addr, 1, 1 # surface row and column + pin-surface-to s-addr, 1, 1 # screen row and column + render-surface s-addr + var screen-ah/eax: (addr handle screen) <- get s-addr, screen + var screen-addr/eax: (addr screen) <- lookup *screen-ah + check-screen-row screen-addr, 1, "abcd", "F - test-surface-pin-at-origin" + check-screen-row screen-addr, 2, "ghij", "F - test-surface-pin-at-origin" + check-screen-row screen-addr, 3, "mnop", "F - test-surface-pin-at-origin" +} + +# pin (1, 1) to (2, 1) on screen; screen goes past edge of the universe +fn test-surface-pin-2 { + var s: surface + var s-addr/esi: (addr surface) <- address s + # surface contents are a fixed grid with 8 rows and 6 columns + # (strip vowels second time around to break vertical alignment of letters) + initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz" + pin-surface-at s-addr, 1, 1 # surface row and column + pin-surface-to s-addr, 2, 1 # screen row and column + render-surface s-addr + var screen-ah/eax: (addr handle screen) <- get s-addr, screen + var screen-addr/eax: (addr screen) <- lookup *screen-ah + # surface edge reached (should seldom happen in the app) + check-screen-row screen-addr, 1, " ", "F - test-surface-pin-2" + check-screen-row screen-addr, 2, "abcd", "F - test-surface-pin-2" + check-screen-row screen-addr, 3, "ghij", "F - test-surface-pin-2" +} + +# pin (2, 1) to (1, 1) on screen +fn test-surface-pin-3 { + var s: surface + var s-addr/esi: (addr surface) <- address s + # surface contents are a fixed grid with 8 rows and 6 columns + # (strip vowels second time around to break vertical alignment of letters) + initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz" + pin-surface-at s-addr, 2, 1 # surface row and column + pin-surface-to s-addr, 1, 1 # screen row and column + render-surface s-addr + var screen-ah/eax: (addr handle screen) <- get s-addr, screen + var screen-addr/eax: (addr screen) <- lookup *screen-ah + check-screen-row screen-addr, 1, "ghij", "F - test-surface-pin-3" + check-screen-row screen-addr, 2, "mnop", "F - test-surface-pin-3" + check-screen-row screen-addr, 3, "stuv", "F - test-surface-pin-3" +} + +# pin (1, 1) to (1, 2) on screen; screen goes past edge of the universe +fn test-surface-pin-4 { + var s: surface + var s-addr/esi: (addr surface) <- address s + # surface contents are a fixed grid with 8 rows and 6 columns + # (strip vowels second time around to break vertical alignment of letters) + initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz" + pin-surface-at s-addr, 1, 1 # surface row and column + pin-surface-to s-addr, 1, 2 # screen row and column + render-surface s-addr + var screen-ah/eax: (addr handle screen) <- get s-addr, screen + var screen-addr/eax: (addr screen) <- lookup *screen-ah + # surface edge reached (should seldom happen in the app) + check-screen-row screen-addr, 1, " abc", "F - test-surface-pin-4" + check-screen-row screen-addr, 2, " ghi", "F - test-surface-pin-4" + check-screen-row screen-addr, 3, " mno", "F - test-surface-pin-4" +} + +# pin (1, 2) to (1, 1) on screen +fn test-surface-pin-5 { + var s: surface + var s-addr/esi: (addr surface) <- address s + # surface contents are a fixed grid with 8 rows and 6 columns + # (strip vowels second time around to break vertical alignment of letters) + initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz" + pin-surface-at s-addr, 1, 2 # surface row and column + pin-surface-to s-addr, 1, 1 # screen row and column + render-surface s-addr + var screen-ah/eax: (addr handle screen) <- get s-addr, screen + var screen-addr/eax: (addr screen) <- lookup *screen-ah + check-screen-row screen-addr, 1, "bcde", "F - test-surface-pin-5" + check-screen-row screen-addr, 2, "hijk", "F - test-surface-pin-5" + check-screen-row screen-addr, 3, "nopq", "F - test-surface-pin-5" +} + +fn initialize-surface-with-fake-screen _self: (addr surface), nrows: int, ncols: int, in: (addr array byte) { + var self/esi: (addr surface) <- copy _self + # fill in screen + var screen-ah/eax: (addr handle screen) <- get self, screen + allocate screen-ah + var screen-addr/eax: (addr screen) <- lookup *screen-ah + initialize-screen screen-addr, nrows, ncols + # fill in everything else + initialize-surface-with self, in +} diff --git a/linux/tile/table.mu b/linux/tile/table.mu new file mode 100644 index 00000000..9c03117b --- /dev/null +++ b/linux/tile/table.mu @@ -0,0 +1,165 @@ +fn initialize-table _self: (addr table), n: int { + var self/esi: (addr table) <- copy _self + var data-ah/eax: (addr handle array bind) <- get self, data + populate data-ah, n +} + +fn deep-copy-table _src: (addr table), _dest: (addr table) { +#? print-string 0, "deep-copy-table\n" + var src/eax: (addr table) <- copy _src + var src-data-ah/eax: (addr handle array bind) <- get src, data + var _src-data/eax: (addr array bind) <- lookup *src-data-ah + var src-data/esi: (addr array bind) <- copy _src-data + var n/ecx: int <- length src-data + var dest/eax: (addr table) <- copy _dest + initialize-table dest, n + var dest-data-ah/eax: (addr handle array bind) <- get dest, data + var _dest-data/eax: (addr array bind) <- lookup *dest-data-ah + var dest-data/edi: (addr array bind) <- copy _dest-data + var i/eax: int <- copy 0 + { + compare i, n + break-if->= +#? print-string 0, "iter\n" + $deep-copy:element: { + var offset/edx: (offset bind) <- compute-offset src-data, i + var src-bind/ecx: (addr bind) <- index src-data, offset + var dest-bind/edx: (addr bind) <- index dest-data, offset + var src-key-ah/ebx: (addr handle array byte) <- get src-bind, key + var src-key/eax: (addr array byte) <- lookup *src-key-ah + compare src-key, 0 + break-if-= + # copy key + var dest-key-ah/eax: (addr handle array byte) <- get dest-bind, key + copy-object src-key-ah, dest-key-ah + # deep copy value + var src-val-ah/eax: (addr handle value) <- get src-bind, value + var _src-val/eax: (addr value) <- lookup *src-val-ah + var src-val/ecx: (addr value) <- copy _src-val + var dest-val-ah/eax: (addr handle value) <- get dest-bind, value + allocate dest-val-ah + var dest-val/eax: (addr value) <- lookup *dest-val-ah +#? print-string 0, "deep copy value {\n" + deep-copy-value src-val, dest-val +#? print-string 0, "}\n" + } + i <- increment + loop + } +#? print-string 0, "end deep-copy-table\n" +} + +fn bind-in-table _self: (addr table), key: (addr handle array byte), val: (addr value) { + var self/esi: (addr table) <- copy _self + var data-ah/esi: (addr handle array bind) <- get self, data + var _data/eax: (addr array bind) <- lookup *data-ah + var data/esi: (addr array bind) <- copy _data + var next-empty-slot-index/eax: (offset bind) <- next-empty-slot data, key + var dest/eax: (addr bind) <- index data, next-empty-slot-index + make-binding dest, key, val +} + +# manual test: full array of binds +fn next-empty-slot _data: (addr array bind), key: (addr handle array byte) -> _/eax: (offset bind) { + var data/esi: (addr array bind) <- copy _data + var len/ecx: int <- length data + var i/edx: int <- copy 0 + var result/eax: (offset bind) <- copy 0 + $next-empty-slot:loop: { + result <- compute-offset data, i + compare i, len + break-if->= + { + var target/esi: (addr bind) <- index data, result + var target2/esi: (addr handle array byte) <- get target, key + var target3/eax: (addr array byte) <- lookup *target2 + compare target3, 0 + break-if-= $next-empty-slot:loop + # TODO: how to indicate that key already exists? we don't want to permit rebinding + } + i <- increment + loop + } + return result +} + +fn make-number-binding _self: (addr bind), key: (addr handle array byte), _val: float { + var self/esi: (addr bind) <- copy _self + var dest/eax: (addr handle array byte) <- get self, key + copy-object key, dest + var dest2/eax: (addr handle value) <- get self, value + allocate dest2 + var dest3/eax: (addr value) <- lookup *dest2 + var dest4/eax: (addr float) <- get dest3, number-data + var val/xmm0: float <- copy _val + copy-to *dest4, val +} + +fn make-binding _self: (addr bind), key: (addr handle array byte), val: (addr value) { + var self/esi: (addr bind) <- copy _self + var dest/eax: (addr handle array byte) <- get self, key + copy-object key, dest + var dest2/eax: (addr handle value) <- get self, value + allocate dest2 + var dest3/eax: (addr value) <- lookup *dest2 + copy-object val, dest3 +} + +fn lookup-binding _self: (addr table), key: (addr array byte), out: (addr handle value) { + var self/esi: (addr table) <- copy _self + var data-ah/esi: (addr handle array bind) <- get self, data + var _data/eax: (addr array bind) <- lookup *data-ah + var data/esi: (addr array bind) <- copy _data + var len/edx: int <- length data + var i/ebx: int <- copy 0 + $lookup-binding:loop: { + compare i, len + break-if->= + { + var offset/edx: (offset bind) <- compute-offset data, i + var target-bind/esi: (addr bind) <- index data, offset + var target2/edx: (addr handle array byte) <- get target-bind, key + var target3/eax: (addr array byte) <- lookup *target2 + compare target3, 0 + break-if-= $lookup-binding:loop + var is-match?/eax: boolean <- string-equal? target3, key + compare is-match?, 0/false + break-if-= + # found + var target/eax: (addr handle value) <- get target-bind, value + copy-object target, out + break $lookup-binding:loop + } + i <- increment + loop + } +} + +fn dump-table _self: (addr table) { + var self/esi: (addr table) <- copy _self + var data-ah/esi: (addr handle array bind) <- get self, data + var _data/eax: (addr array bind) <- lookup *data-ah + var data/esi: (addr array bind) <- copy _data + var len/edx: int <- length data + var i/ebx: int <- copy 0 + { + compare i, len + break-if->= + var offset/edx: (offset bind) <- compute-offset data, i + var target-bind/esi: (addr bind) <- index data, offset + var key-ah/edx: (addr handle array byte) <- get target-bind, key + var key/eax: (addr array byte) <- lookup *key-ah + compare key, 0 + break-if-= + print-string 0, key + print-string 0, ": " + var val-ah/eax: (addr handle value) <- get target-bind, value + var val/eax: (addr value) <- lookup *val-ah + var type/eax: (addr int) <- get val, type + print-int32-hex 0, *type + print-string 0, "\n" + i <- increment + loop + } + print-string 0, "\n" +} diff --git a/linux/tile/value-stack.mu b/linux/tile/value-stack.mu new file mode 100644 index 00000000..886b4037 --- /dev/null +++ b/linux/tile/value-stack.mu @@ -0,0 +1,149 @@ +# support for non-int values is untested + +type value-stack { + data: (handle array value) + top: int +} + +fn initialize-value-stack _self: (addr value-stack), n: int { + var self/esi: (addr value-stack) <- copy _self + var d/edi: (addr handle array value) <- get self, data + populate d, n + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn clear-value-stack _self: (addr value-stack) { + var self/esi: (addr value-stack) <- copy _self + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn push-number-to-value-stack _self: (addr value-stack), _val: float { + var self/esi: (addr value-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array value) <- get self, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var dest-addr/edx: (addr value) <- index data, dest-offset + var dest-addr2/eax: (addr float) <- get dest-addr, number-data + var val/xmm0: float <- copy _val +#? print-float-decimal-approximate 0, val, 3 + copy-to *dest-addr2, val + increment *top-addr + var type-addr/eax: (addr int) <- get dest-addr, type + copy-to *type-addr, 0/number +} + +fn push-string-to-value-stack _self: (addr value-stack), val: (handle array byte) { + var self/esi: (addr value-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array value) <- get self, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var dest-addr/edx: (addr value) <- index data, dest-offset + var dest-addr2/eax: (addr handle array byte) <- get dest-addr, text-data + copy-handle val, dest-addr2 + var dest-addr3/eax: (addr int) <- get dest-addr, type +#? print-string 0, "setting type to 1: " +#? { +#? var foo/eax: int <- copy dest-addr3 +#? print-int32-hex 0, foo +#? } +#? print-string 0, "\n" + copy-to *dest-addr3, 1/string + increment *top-addr +} + +fn push-array-to-value-stack _self: (addr value-stack), val: (handle array value) { + var self/esi: (addr value-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array value) <- get self, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var dest-addr/edx: (addr value) <- index data, dest-offset + var dest-addr2/eax: (addr handle array value) <- get dest-addr, array-data + copy-handle val, dest-addr2 + # update type + var dest-addr3/eax: (addr int) <- get dest-addr, type + copy-to *dest-addr3, 2/array + increment *top-addr +} + +fn push-value-stack _self: (addr value-stack), val: (addr value) { + var self/esi: (addr value-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array value) <- get self, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var dest-addr/edx: (addr value) <- index data, dest-offset + copy-object val, dest-addr + increment *top-addr +} + +fn pop-number-from-value-stack _self: (addr value-stack) -> _/xmm0: float { + var self/esi: (addr value-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + { + compare *top-addr, 0 + break-if-> + var minus-one/eax: int <- copy -1 + var minus-one-f/xmm0: float <- convert minus-one + return minus-one-f + } + decrement *top-addr + var data-ah/edx: (addr handle array value) <- get self, data + var data/eax: (addr array value) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-offset/edx: (offset value) <- compute-offset data, top + var result-addr/eax: (addr value) <- index data, dest-offset + var result-addr2/eax: (addr float) <- get result-addr, number-data + return *result-addr2 +} + +fn value-stack-empty? _self: (addr value-stack) -> _/eax: boolean { + var self/esi: (addr value-stack) <- copy _self + var top/eax: (addr int) <- get self, top + compare *top, 0 + { + break-if-!= + return 1/true + } + return 0/false +} + +fn value-stack-length _self: (addr value-stack) -> _/eax: int { + var self/esi: (addr value-stack) <- copy _self + var top-addr/eax: (addr int) <- get self, top + return *top-addr +} + +fn save-lines in-h: (handle array (handle array byte)), _out-ah: (addr handle array value) { + var _in/eax: (addr array (handle array byte)) <- lookup in-h + var in/esi: (addr array (handle array byte)) <- copy _in + var len/ecx: int <- length in + var out-ah/edi: (addr handle array value) <- copy _out-ah + populate out-ah, len + var out/eax: (addr array value) <- lookup *out-ah + # copy in into out + var i/ebx: int <- copy 0 + { + compare i, len + break-if->= +#? print-int32-hex 0, i +#? print-string 0, "\n" + var src/ecx: (addr handle array byte) <- index in, i + var dest-offset/edx: (offset value) <- compute-offset out, i + var dest-val/edx: (addr value) <- index out, dest-offset + var dest/eax: (addr handle array byte) <- get dest-val, text-data + copy-object src, dest + var type/edx: (addr int) <- get dest-val, type + copy-to *type, 1/string + i <- increment + loop + } +} diff --git a/linux/tile/value.mu b/linux/tile/value.mu new file mode 100644 index 00000000..8bd01676 --- /dev/null +++ b/linux/tile/value.mu @@ -0,0 +1,424 @@ +fn render-value-at screen: (addr screen), row: int, col: int, _val: (addr value), top-level?: boolean { + move-cursor screen, row, col + var val/esi: (addr value) <- copy _val + var val-type/ecx: (addr int) <- get val, type + # per-type rendering logic goes here + compare *val-type, 1/string + { + break-if-!= + var val-ah/eax: (addr handle array byte) <- get val, text-data + var val-string/eax: (addr array byte) <- lookup *val-ah + compare val-string, 0 + break-if-= + var orig-len/ecx: int <- length val-string + var truncated: (handle array byte) + var truncated-ah/esi: (addr handle array byte) <- address truncated + substring val-string, 0, 0xc, truncated-ah + var truncated-string/eax: (addr array byte) <- lookup *truncated-ah + var len/edx: int <- length truncated-string + start-color screen, 0xf2, 7 + print-code-point screen, 0x275d/open-quote + print-string screen, truncated-string + compare len, orig-len + { + break-if-= + print-code-point screen, 0x2026/ellipses + } + print-code-point screen, 0x275e/close-quote + reset-formatting screen + return + } + compare *val-type, 2/array + { + break-if-!= + var val-ah/eax: (addr handle array value) <- get val, array-data + var val-array/eax: (addr array value) <- lookup *val-ah + render-array-at screen, row, col, val-array + return + } + compare *val-type, 3/file + { + break-if-!= + var val-ah/eax: (addr handle buffered-file) <- get val, file-data + var val-file/eax: (addr buffered-file) <- lookup *val-ah + start-color screen, 0, 7 + # TODO + print-string screen, " FILE " + return + } + compare *val-type, 4/screen + { + break-if-!= +#? print-string 0, "render-screen" + var val-ah/eax: (addr handle screen) <- get val, screen-data + var val-screen/eax: (addr screen) <- lookup *val-ah + render-screen screen, row, col, val-screen +#? print-string 0, "}\n" + return + } + # render ints by default for now + var val-num/eax: (addr float) <- get val, number-data + render-number screen, *val-num, top-level? +} + +# synaesthesia +# TODO: right-justify +fn render-number screen: (addr screen), val: float, top-level?: boolean { + # if we're inside an array, don't color + compare top-level?, 0 + { + break-if-!= + print-float-decimal-approximate screen, val, 3 + return + } + var val-int/eax: int <- convert val + var bg/eax: int <- hash-color val-int + var fg/ecx: int <- copy 7 + { + compare bg, 2 + break-if-!= + fg <- copy 0 + } + { + compare bg, 3 + break-if-!= + fg <- copy 0 + } + { + compare bg, 6 + break-if-!= + fg <- copy 0 + } + start-color screen, fg, bg + print-grapheme screen, 0x20/space + print-float-decimal-approximate screen, val, 3 + print-grapheme screen, 0x20/space +} + +fn render-array-at screen: (addr screen), row: int, col: int, _a: (addr array value) { + start-color screen, 0xf2, 7 + # don't surround in spaces + print-grapheme screen, 0x5b/[ + increment col + var a/esi: (addr array value) <- copy _a + var max/ecx: int <- length a + var i/eax: int <- copy 0 + { + compare i, max + break-if->= + { + compare i, 0 + break-if-= + print-string screen, " " + } + var off/ecx: (offset value) <- compute-offset a, i + var x/ecx: (addr value) <- index a, off + render-value-at screen, row, col, x, 0 + { + var w/eax: int <- value-width x, 0 + add-to col, w + increment col + } + i <- increment + loop + } + print-grapheme screen, 0x5d/] +} + +fn render-screen screen: (addr screen), row: int, col: int, _target-screen: (addr screen) { + reset-formatting screen + move-cursor screen, row, col + var target-screen/esi: (addr screen) <- copy _target-screen + var ncols-a/ecx: (addr int) <- get target-screen, num-cols + print-upper-border screen, *ncols-a + var r/edx: int <- copy 1 + var nrows-a/ebx: (addr int) <- get target-screen, num-rows + { + compare r, *nrows-a + break-if-> + increment row # mutate arg + move-cursor screen, row, col + print-string screen, " " + var c/edi: int <- copy 1 + { + compare c, *ncols-a + break-if-> + print-screen-cell-of-fake-screen screen, target-screen, r, c + c <- increment + loop + } + print-string screen, " " + r <- increment + loop + } + increment row # mutate arg + move-cursor screen, row, col + print-lower-border screen, *ncols-a +} + +fn hash-color val: int -> _/eax: int { + var quotient/eax: int <- copy 0 + var remainder/edx: int <- copy 0 + quotient, remainder <- integer-divide val, 7 # assumes that 7 is always the background color + return remainder +} + +fn print-screen-cell-of-fake-screen screen: (addr screen), _target: (addr screen), _row: int, _col: int { + start-color screen, 0, 0xf6 + var target/esi: (addr screen) <- copy _target + var row/ecx: int <- copy _row + var col/edx: int <- copy _col + # if cursor is at screen-cell, add some fancy + { + var cursor-row/eax: (addr int) <- get target, cursor-row + compare *cursor-row, row + break-if-!= + var cursor-col/eax: (addr int) <- get target, cursor-col + compare *cursor-col, col + break-if-!= + start-blinking screen + start-color screen, 0, 1 + } + var g/eax: grapheme <- screen-grapheme-at target, row, col + { + compare g, 0 + break-if-!= + g <- copy 0x20/space + } + print-grapheme screen, g + reset-formatting screen +} + +fn print-upper-border screen: (addr screen), width: int { + print-code-point screen, 0x250c/top-left-corner + var i/eax: int <- copy 0 + { + compare i, width + break-if->= + print-code-point screen, 0x2500/horizontal-line + i <- increment + loop + } + print-code-point screen, 0x2510/top-right-corner +} + +fn print-lower-border screen: (addr screen), width: int { + print-code-point screen, 0x2514/bottom-left-corner + var i/eax: int <- copy 0 + { + compare i, width + break-if->= + print-code-point screen, 0x2500/horizontal-line + i <- increment + loop + } + print-code-point screen, 0x2518/bottom-right-corner +} + +fn value-width _v: (addr value), top-level: boolean -> _/eax: int { + var v/esi: (addr value) <- copy _v + var type/eax: (addr int) <- get v, type + { + compare *type, 0/int + break-if-!= + var v-num/edx: (addr float) <- get v, number-data + var result/eax: int <- float-size *v-num, 3 + return result + } + { + compare *type, 1/string + break-if-!= + var s-ah/eax: (addr handle array byte) <- get v, text-data + var s/eax: (addr array byte) <- lookup *s-ah + compare s, 0 + break-if-= + var result/eax: int <- length s + compare result, 0xd/max-string-size + { + break-if-<= + result <- copy 0xd + } + # if it's a nested string, include space for quotes + # we don't do this for the top-level, where the quotes will overflow + # into surrounding padding. + compare top-level, 0/false + { + break-if-!= + result <- add 2 + } + return result + } + { + compare *type, 2/array + break-if-!= + var a-ah/eax: (addr handle array value) <- get v, array-data + var a/eax: (addr array value) <- lookup *a-ah + compare a, 0 + break-if-= + var result/eax: int <- array-width a + return result + } + { + compare *type, 3/file + break-if-!= + var f-ah/eax: (addr handle buffered-file) <- get v, file-data + var f/eax: (addr buffered-file) <- lookup *f-ah + compare f, 0 + break-if-= + # TODO: visualizing file handles + return 4 + } + { + compare *type, 4/screen + break-if-!= + var screen-ah/eax: (addr handle screen) <- get v, screen-data + var screen/eax: (addr screen) <- lookup *screen-ah + compare screen, 0 + break-if-= + var ncols/ecx: (addr int) <- get screen, num-cols + var result/eax: int <- copy *ncols + result <- add 2 # left/right margins + return *ncols + } + return 0 +} + +# keep sync'd with render-array-at +fn array-width _a: (addr array value) -> _/eax: int { + var a/esi: (addr array value) <- copy _a + var max/ecx: int <- length a + var i/eax: int <- copy 0 + var result/edi: int <- copy 0 + { + compare i, max + break-if->= + { + compare i, 0 + break-if-= + result <- increment # for space + } + var off/ecx: (offset value) <- compute-offset a, i + var x/ecx: (addr value) <- index a, off + { + var w/eax: int <- value-width x, 0 + result <- add w + } + i <- increment + loop + } + # we won't add 2 for surrounding brackets since we don't surround arrays in + # spaces like other value types + return result +} + +fn value-height _v: (addr value) -> _/eax: int { + var v/esi: (addr value) <- copy _v + var type/eax: (addr int) <- get v, type + { + compare *type, 3/file + break-if-!= + # TODO: visualizing file handles + return 1 + } + { + compare *type, 4/screen + break-if-!= + var screen-ah/eax: (addr handle screen) <- get v, screen-data + var screen/eax: (addr screen) <- lookup *screen-ah + compare screen, 0 + break-if-= + var nrows/ecx: (addr int) <- get screen, num-rows + var result/eax: int <- copy *nrows + result <- add 2 # top and bottom border + return result + } + return 1 +} + +fn deep-copy-value _src: (addr value), _dest: (addr value) { +#? print-string 0, "deep-copy-value\n" + var src/esi: (addr value) <- copy _src + var dest/edi: (addr value) <- copy _dest + var type/ebx: (addr int) <- get src, type + var y/ecx: (addr int) <- get dest, type + copy-object type, y + compare *type, 0 # int + { + break-if-!= +#? print-string 0, "int value\n" + var src-n/eax: (addr float) <- get src, number-data + var dest-n/ecx: (addr float) <- get dest, number-data + copy-object src-n, dest-n + return + } + compare *type, 1/string + { + break-if-!= +#? print-string 0, "string value\n" + var src-ah/eax: (addr handle array byte) <- get src, text-data + var src/eax: (addr array byte) <- lookup *src-ah + var dest-ah/edx: (addr handle array byte) <- get dest, text-data + copy-array-object src, dest-ah + return + } + compare *type, 2/array + { + break-if-!= +#? print-string 0, "array value\n" + var src-ah/eax: (addr handle array value) <- get src, array-data + var _src/eax: (addr array value) <- lookup *src-ah + var src/esi: (addr array value) <- copy _src + var n/ecx: int <- length src + var dest-ah/edx: (addr handle array value) <- get dest, array-data + populate dest-ah, n + var _dest/eax: (addr array value) <- lookup *dest-ah + var dest/edi: (addr array value) <- copy _dest + var i/eax: int <- copy 0 + { + compare i, n + break-if->= + { + var offset/edx: (offset value) <- compute-offset src, i + var src-element/eax: (addr value) <- index src, offset + var dest-element/ecx: (addr value) <- index dest, offset + deep-copy-value src-element, dest-element + } + i <- increment + loop + } + copy-array-object src, dest-ah + return + } + compare *type, 3/file + { + break-if-!= +#? print-string 0, "file value\n" + var src-filename-ah/eax: (addr handle array byte) <- get src, filename + var _src-filename/eax: (addr array byte) <- lookup *src-filename-ah + var src-filename/ecx: (addr array byte) <- copy _src-filename + var dest-filename-ah/ebx: (addr handle array byte) <- get dest, filename + copy-array-object src-filename, dest-filename-ah + var src-file-ah/eax: (addr handle buffered-file) <- get src, file-data + var src-file/eax: (addr buffered-file) <- lookup *src-file-ah + var dest-file-ah/edx: (addr handle buffered-file) <- get dest, file-data + copy-file src-file, dest-file-ah, src-filename + return + } + compare *type, 4/screen + { + break-if-!= +#? print-string 0, "screen value\n" + var src-screen-ah/eax: (addr handle screen) <- get src, screen-data + var _src-screen/eax: (addr screen) <- lookup *src-screen-ah + var src-screen/ecx: (addr screen) <- copy _src-screen + var dest-screen-ah/eax: (addr handle screen) <- get dest, screen-data + allocate dest-screen-ah + var dest-screen/eax: (addr screen) <- lookup *dest-screen-ah + copy-object src-screen, dest-screen + var dest-screen-data-ah/ebx: (addr handle array screen-cell) <- get dest-screen, data + var src-screen-data-ah/eax: (addr handle array screen-cell) <- get src-screen, data + var src-screen-data/eax: (addr array screen-cell) <- lookup *src-screen-data-ah + copy-array-object src-screen-data, dest-screen-data-ah + return + } +} diff --git a/linux/tile/vimrc.vim b/linux/tile/vimrc.vim new file mode 100644 index 00000000..348fe364 --- /dev/null +++ b/linux/tile/vimrc.vim @@ -0,0 +1,2 @@ +" when opening files in this directory, load vimrc from cwd (top-level) +source vimrc.vim diff --git a/linux/tile/word.mu b/linux/tile/word.mu new file mode 100644 index 00000000..b4f5000b --- /dev/null +++ b/linux/tile/word.mu @@ -0,0 +1,573 @@ +fn initialize-word _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + allocate data-ah + var data/eax: (addr gap-buffer) <- lookup *data-ah + initialize-gap-buffer data +} + +## some helpers for creating words. mostly for tests + +fn initialize-word-with _self: (addr word), s: (addr array byte) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + allocate data-ah + var data/eax: (addr gap-buffer) <- lookup *data-ah + initialize-gap-buffer-with data, s +} + +fn allocate-word-with _out: (addr handle word), s: (addr array byte) { + var out/eax: (addr handle word) <- copy _out + allocate out + var out-addr/eax: (addr word) <- lookup *out + initialize-word-with out-addr, s +} + +# just for tests for now +# TODO: handle existing next +# one implication of handles: append must take a handle +fn append-word-with self-h: (handle word), s: (addr array byte) { + var self/eax: (addr word) <- lookup self-h + var next-ah/eax: (addr handle word) <- get self, next + allocate-word-with next-ah, s + var next/eax: (addr word) <- lookup *next-ah + var prev-ah/eax: (addr handle word) <- get next, prev + copy-handle self-h, prev-ah +} + +# just for tests for now +# TODO: handle existing prev +fn prepend-word-with self-h: (handle word), s: (addr array byte) { + var self/eax: (addr word) <- lookup self-h + var prev-ah/eax: (addr handle word) <- get self, prev + allocate-word-with prev-ah, s + var prev/eax: (addr word) <- lookup *prev-ah + var next-ah/eax: (addr handle word) <- get prev, next + copy-handle self-h, next-ah +} + +## real primitives + +fn move-word-contents _src-ah: (addr handle word), _dest-ah: (addr handle word) { + var dest-ah/eax: (addr handle word) <- copy _dest-ah + var _dest/eax: (addr word) <- lookup *dest-ah + var dest/edi: (addr word) <- copy _dest + var src-ah/eax: (addr handle word) <- copy _src-ah + var _src/eax: (addr word) <- lookup *src-ah + var src/esi: (addr word) <- copy _src + cursor-to-start src + var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data + var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah + var src-stack/ecx: (addr grapheme-stack) <- get src-data, right + { + var done?/eax: boolean <- grapheme-stack-empty? src-stack + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- pop-grapheme-stack src-stack +#? print-grapheme 0, g +#? print-string 0, "\n" + add-grapheme-to-word dest, g + loop + } +} + +fn copy-word-contents-before-cursor _src-ah: (addr handle word), _dest-ah: (addr handle word) { + var dest-ah/eax: (addr handle word) <- copy _dest-ah + var _dest/eax: (addr word) <- lookup *dest-ah + var dest/edi: (addr word) <- copy _dest + var src-ah/eax: (addr handle word) <- copy _src-ah + var src/eax: (addr word) <- lookup *src-ah + var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data + var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah + var src-stack/ecx: (addr grapheme-stack) <- get src-data, left + var src-stack-data-ah/eax: (addr handle array grapheme) <- get src-stack, data + var _src-stack-data/eax: (addr array grapheme) <- lookup *src-stack-data-ah + var src-stack-data/edx: (addr array grapheme) <- copy _src-stack-data + var top-addr/ecx: (addr int) <- get src-stack, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index src-stack-data, i + add-grapheme-to-word dest, *g + i <- increment + loop + } +} + +fn word-equal? _self: (addr word), s: (addr array byte) -> _/eax: boolean { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: boolean <- gap-buffer-equal? data, s + return result +} + +fn word-length _self: (addr word) -> _/eax: int { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: int <- gap-buffer-length data + return result +} + +fn final-word _in: (addr handle word), out: (addr handle word) { + var curr-h: (handle word) + var curr-ah/esi: (addr handle word) <- address curr-h + copy-object _in, curr-ah + var curr/eax: (addr word) <- copy 0 + var next/edi: (addr handle word) <- copy 0 + { + curr <- lookup *curr-ah + next <- get curr, next + curr <- lookup *next + compare curr, 0 + break-if-= + copy-object next, curr-ah + loop + } + copy-object curr-ah, out # modify 'out' right at the end, just in case it's same as 'in' +} + +fn first-grapheme _self: (addr word) -> _/eax: grapheme { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: grapheme <- first-grapheme-in-gap-buffer data + return result +} + +fn grapheme-before-cursor _self: (addr word) -> _/eax: grapheme { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: grapheme <- grapheme-before-cursor-in-gap-buffer data + return result +} + +fn add-grapheme-to-word _self: (addr word), c: grapheme { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + add-grapheme-at-gap data, c +} + +fn cursor-at-start? _self: (addr word) -> _/eax: boolean { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: boolean <- gap-at-start? data + return result +} + +fn cursor-at-end? _self: (addr word) -> _/eax: boolean { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: boolean <- gap-at-end? data + return result +} + +fn cursor-left _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var dummy/eax: grapheme <- gap-left data +} + +fn cursor-right _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var dummy/eax: grapheme <- gap-right data +} + +fn cursor-to-start _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + gap-to-start data +} + +fn cursor-to-end _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + gap-to-end data +} + +fn cursor-index _self: (addr word) -> _/eax: int { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: int <- gap-index data + return result +} + +fn delete-before-cursor _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + delete-before-gap data +} + +fn pop-after-cursor _self: (addr word) -> _/eax: grapheme { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: grapheme <- pop-after-gap data + return result +} + +fn delete-next _self: (addr word) { + var self/esi: (addr word) <- copy _self + var next-ah/edi: (addr handle word) <- get self, next + var next/eax: (addr word) <- lookup *next-ah + compare next, 0 + break-if-= + var next-next-ah/ecx: (addr handle word) <- get next, next + var self-ah/esi: (addr handle word) <- get next, prev + copy-object next-next-ah, next-ah + var new-next/eax: (addr word) <- lookup *next-next-ah + compare new-next, 0 + break-if-= + var dest/eax: (addr handle word) <- get new-next, prev + copy-object self-ah, dest +} + +fn print-word screen: (addr screen), _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + render-gap-buffer screen, data +} + +fn print-words-in-reverse screen: (addr screen), _words-ah: (addr handle word) { + var words-ah/eax: (addr handle word) <- copy _words-ah + var words-a/eax: (addr word) <- lookup *words-ah + compare words-a, 0 + break-if-= + # recurse + var next-ah/ecx: (addr handle word) <- get words-a, next + print-words-in-reverse screen, next-ah + # print + print-word screen, words-a + print-string screen, " " +} + +# Gotcha with some word operations: ensure dest-ah isn't in the middle of some +# existing chain of words. There are two pointers to patch, and you'll forget +# to do the other one. +fn copy-words _src-ah: (addr handle word), _dest-ah: (addr handle word) { + var src-ah/eax: (addr handle word) <- copy _src-ah + var src-a/eax: (addr word) <- lookup *src-ah + compare src-a, 0 + break-if-= + # copy + var dest-ah/edi: (addr handle word) <- copy _dest-ah + copy-word src-a, dest-ah + # recurse + var rest: (handle word) + var rest-ah/ecx: (addr handle word) <- address rest + var next-src-ah/esi: (addr handle word) <- get src-a, next + copy-words next-src-ah, rest-ah + chain-words dest-ah, rest-ah +} + +fn copy-words-in-reverse _src-ah: (addr handle word), _dest-ah: (addr handle word) { + var src-ah/eax: (addr handle word) <- copy _src-ah + var _src-a/eax: (addr word) <- lookup *src-ah + var src-a/esi: (addr word) <- copy _src-a + compare src-a, 0 + break-if-= + # recurse + var next-src-ah/ecx: (addr handle word) <- get src-a, next + var dest-ah/edi: (addr handle word) <- copy _dest-ah + copy-words-in-reverse next-src-ah, dest-ah + # + copy-word-at-end src-a, dest-ah +} + +fn copy-word-at-end src: (addr word), _dest-ah: (addr handle word) { + var dest-ah/edi: (addr handle word) <- copy _dest-ah + # if dest is null, copy and return + var dest-a/eax: (addr word) <- lookup *dest-ah + compare dest-a, 0 + { + break-if-!= + copy-word src, dest-ah + return + } + # copy current word + var new: (handle word) + var new-ah/ecx: (addr handle word) <- address new + copy-word src, new-ah + # append it at the end + var curr-ah/edi: (addr handle word) <- copy dest-ah + { + var curr-a/eax: (addr word) <- lookup *curr-ah # curr-a guaranteed not to be null + var next-ah/ecx: (addr handle word) <- get curr-a, next + var next-a/eax: (addr word) <- lookup *next-ah + compare next-a, 0 + break-if-= + curr-ah <- copy next-ah + loop + } + chain-words curr-ah, new-ah +} + +fn append-word-at-end-with _dest-ah: (addr handle word), s: (addr array byte) { + var dest-ah/edi: (addr handle word) <- copy _dest-ah + # if dest is null, copy and return + var dest-a/eax: (addr word) <- lookup *dest-ah + compare dest-a, 0 + { + break-if-!= + allocate-word-with dest-ah, s + return + } + # otherwise append at end + var curr-ah/edi: (addr handle word) <- copy dest-ah + { + var curr-a/eax: (addr word) <- lookup *curr-ah # curr-a guaranteed not to be null + var next-ah/ecx: (addr handle word) <- get curr-a, next + var next-a/eax: (addr word) <- lookup *next-ah + compare next-a, 0 + break-if-= + curr-ah <- copy next-ah + loop + } + append-word-with *curr-ah, s +} + +fn copy-word _src-a: (addr word), _dest-ah: (addr handle word) { + var dest-ah/eax: (addr handle word) <- copy _dest-ah + allocate dest-ah + var _dest-a/eax: (addr word) <- lookup *dest-ah + var dest-a/eax: (addr word) <- copy _dest-a + initialize-word dest-a + var dest/edi: (addr handle gap-buffer) <- get dest-a, scalar-data + var src-a/eax: (addr word) <- copy _src-a + var src/eax: (addr handle gap-buffer) <- get src-a, scalar-data + copy-gap-buffer src, dest +} + +# one implication of handles: append must take a handle +fn append-word _self-ah: (addr handle word) { + var saved-self-storage: (handle word) + var saved-self/eax: (addr handle word) <- address saved-self-storage + copy-object _self-ah, saved-self +#? { +#? print-string 0, "self-ah is " +#? var foo/eax: int <- copy _self-ah +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + var self-ah/esi: (addr handle word) <- copy _self-ah + var _self/eax: (addr word) <- lookup *self-ah + var self/ebx: (addr word) <- copy _self +#? { +#? print-string 0, "0: self is " +#? var self-ah/eax: (addr handle word) <- copy _self-ah +#? var self/eax: (addr word) <- lookup *self-ah +#? var foo/eax: int <- copy self +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + # allocate new handle + var new: (handle word) + var new-ah/ecx: (addr handle word) <- address new + allocate new-ah + var new-addr/eax: (addr word) <- lookup new + initialize-word new-addr +#? { +#? print-string 0, "new is " +#? var foo/eax: int <- copy new-addr +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + # new->next = self->next + var src/esi: (addr handle word) <- get self, next +#? { +#? print-string 0, "src is " +#? var foo/eax: int <- copy src +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + var dest/edi: (addr handle word) <- get new-addr, next + copy-object src, dest + # new->next->prev = new + { + var next-addr/eax: (addr word) <- lookup *src + compare next-addr, 0 + break-if-= +#? { +#? print-string 0, "next-addr is " +#? var foo/eax: int <- copy next-addr +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + dest <- get next-addr, prev +#? #? { +#? #? print-string 0, "self-ah is " +#? #? var foo/eax: int <- copy _self-ah +#? #? print-int32-hex 0, foo +#? #? print-string 0, "\n" +#? #? print-string 0, "2: self is " +#? #? var self-ah/eax: (addr handle word) <- copy _self-ah +#? #? var self/eax: (addr word) <- lookup *self-ah +#? #? var foo/eax: int <- copy self +#? #? print-int32-hex 0, foo +#? #? print-string 0, "\n" +#? #? } +#? { +#? print-string 0, "copying new to " +#? var foo/eax: int <- copy dest +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + copy-object new-ah, dest +#? { +#? print-string 0, "4: self is " +#? var self-ah/eax: (addr handle word) <- copy _self-ah +#? var self/eax: (addr word) <- lookup *self-ah +#? var foo/eax: int <- copy self +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + } + # new->prev = saved-self + dest <- get new-addr, prev +#? { +#? print-string 0, "copying " +#? var self-ah/esi: (addr handle word) <- copy _self-ah +#? var self/eax: (addr word) <- lookup *self-ah +#? var foo/eax: int <- copy self +#? print-int32-hex 0, foo +#? print-string 0, " to " +#? foo <- copy dest +#? print-int32-hex 0, foo +#? print-string 0, "\n" +#? } + var saved-self-ah/eax: (addr handle word) <- address saved-self-storage + copy-object saved-self-ah, dest + # self->next = new + dest <- get self, next + copy-object new-ah, dest +} + +fn chain-words _self-ah: (addr handle word), _next: (addr handle word) { + var self-ah/esi: (addr handle word) <- copy _self-ah + var _self/eax: (addr word) <- lookup *self-ah + var self/ecx: (addr word) <- copy _self + var dest/edx: (addr handle word) <- get self, next + var next-ah/edi: (addr handle word) <- copy _next + copy-object next-ah, dest + var next/eax: (addr word) <- lookup *next-ah + compare next, 0 + break-if-= + dest <- get next, prev + copy-object self-ah, dest +} + +fn emit-word _self: (addr word), out: (addr stream byte) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + emit-gap-buffer data, out +} + +fn word-to-string _self: (addr word), out: (addr handle array byte) { + var self/esi: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + gap-buffer-to-string data, out +} + +fn word-is-decimal-integer? _self: (addr word) -> _/eax: boolean { + var self/eax: (addr word) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data + var data/eax: (addr gap-buffer) <- lookup *data-ah + var result/eax: boolean <- gap-buffer-is-decimal-integer? data + return result +} + +# ABSOLUTELY GHASTLY +fn word-exists? _haystack-ah: (addr handle word), _needle: (addr word) -> _/ebx: boolean { + var needle-name-storage: (handle array byte) + var needle-name-ah/eax: (addr handle array byte) <- address needle-name-storage + word-to-string _needle, needle-name-ah # profligate leak + var _needle-name/eax: (addr array byte) <- lookup *needle-name-ah + var needle-name/edi: (addr array byte) <- copy _needle-name + # base case + var haystack-ah/esi: (addr handle word) <- copy _haystack-ah + var curr/eax: (addr word) <- lookup *haystack-ah + compare curr, 0 + { + break-if-!= + return 0/false + } + # check curr + var curr-name-storage: (handle array byte) + var curr-name-ah/ecx: (addr handle array byte) <- address curr-name-storage + word-to-string curr, curr-name-ah # profligate leak + var curr-name/eax: (addr array byte) <- lookup *curr-name-ah + var found?/eax: boolean <- string-equal? needle-name, curr-name + compare found?, 0 + { + break-if-= + return 1/true + } + # recurse + var curr/eax: (addr word) <- lookup *haystack-ah + var next-haystack-ah/eax: (addr handle word) <- get curr, next + var result/ebx: boolean <- word-exists? next-haystack-ah, _needle + return result +} + +fn word-list-length words: (addr handle word) -> _/eax: int { + var curr-ah/esi: (addr handle word) <- copy words + var result/edi: int <- copy 0 + { + var curr/eax: (addr word) <- lookup *curr-ah + compare curr, 0 + break-if-= + { + var word-len/eax: int <- word-length curr + result <- add word-len + result <- add 1/inter-word-margin + } + curr-ah <- get curr, next + loop + } + return result +} + +# out-ah already has a word allocated and initialized +fn parse-words in: (addr array byte), out-ah: (addr handle word) { + var in-stream: (stream byte 0x100) + var in-stream-a/esi: (addr stream byte) <- address in-stream + write in-stream-a, in + var cursor-word-ah/ebx: (addr handle word) <- copy out-ah + $parse-words:loop: { + var done?/eax: boolean <- stream-empty? in-stream-a + compare done?, 0/false + break-if-!= + var _g/eax: grapheme <- read-grapheme in-stream-a + var g/ecx: grapheme <- copy _g + # if not space, insert + compare g, 0x20/space + { + break-if-= + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + add-grapheme-to-word cursor-word, g + loop $parse-words:loop + } + # otherwise insert word after and move cursor to it + append-word cursor-word-ah + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + cursor-to-start cursor-word # reset cursor in each function + cursor-word-ah <- get cursor-word, next + loop + } +} |