7759 - changing course; delete the postfix shell

It might be too ambitious for an initial Mu system, and I also want to
watch my novelty budget. I also have great doubts about the ability of
this live-updating postfix system to scale to interesting programs. Conditionals,
loops, multi-line functions, all this requires further work.

Instead, I'm going to recenter around Mu's original goals:
  - saying no to most features
  - encouraging/teaching testing
  - traces as a unifying metaphor

In particular, instead of a live-updating system, the new debug loop will
be:
  - generate a trace
  - browse the trace
  - modify the program
  - generate a trace
  - ...

The only persistence we'll need here is a way to track what the programmer
has drilled into in the trace. That might have some commonalities with
the old system of expanded words.
This commit is contained in:
Kartik K. Agaram 2021-02-20 22:14:06 -08:00
parent bcb2190ec3
commit 4a37291e2d
5 changed files with 0 additions and 3358 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,275 +0,0 @@
type line {
name: (handle array byte)
data: (handle word)
cursor: (handle word)
next: (handle line)
prev: (handle line)
}
# 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 cursor-ah/ecx: (addr handle word) <- get line, cursor
copy-object word-ah, cursor-ah
var word/eax: (addr word) <- lookup *word-ah
initialize-word word
}
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 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
}
fn render-line screen: (addr screen), _line: (addr line), x: int, y: int, render-cursor?: boolean -> _/eax: int {
var line/eax: (addr line) <- copy _line
var first-word-ah/esi: (addr handle word) <- get line, data
# cursor-word
var cursor-word/edi: int <- copy 0
compare render-cursor?, 0/false
{
break-if-=
var cursor-word-ah/eax: (addr handle word) <- get line, cursor
var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
cursor-word <- copy _cursor-word
}
#
var result/eax: int <- render-words screen, first-word-ah, x, y, cursor-word
return result
}
fn parse-line in: (addr array byte), _out: (addr line) {
var out/edi: (addr line) <- copy _out
initialize-line out
var dest/eax: (addr handle word) <- get out, data
parse-words in, dest
}
#? fn main {
#? # line = [aaa, bbb, ccc, ddd]
#? var line-storage: line
#? var w-ah/eax: (addr handle word) <- get line-storage, data
#? allocate-word-with w-ah, "aaa"
#? append-word-at-end-with w-ah, "bbb"
#? append-word-at-end-with w-ah, "ccc"
#? append-word-at-end-with w-ah, "ddd"
#? var cursor-ah/ecx: (addr handle word) <- get line-storage, cursor
#? var w/eax: (addr word) <- lookup *w-ah
#? var next-ah/eax: (addr handle word) <- get w, next
#? copy-object next-ah, cursor-ah
#? var line-addr/eax: (addr line) <- address line-storage
#? var dummy/eax: int <- render-line 0/screen, line-addr, 0/x, 0/y, 1/render-cursor
#? }
fn render-line-with-stack screen: (addr screen), _line: (addr line), x: int, y: int, render-cursor?: boolean -> _/eax: int, _/ecx: int {
var line/esi: (addr line) <- copy _line
# cursor-word
var cursor-word/edi: int <- copy 0
compare render-cursor?, 0/false
{
break-if-=
var cursor-word-ah/eax: (addr handle word) <- get line, cursor
var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
cursor-word <- copy _cursor-word
}
#
var curr-word-ah/eax: (addr handle word) <- get line, data
var _curr-word/eax: (addr word) <- lookup *curr-word-ah
var curr-word/edx: (addr word) <- copy _curr-word
var new-x/eax: int <- copy x # increases each iteration
var new-y/ebx: int <- copy y # compute max across all iterations
{
compare curr-word, 0
break-if-=
var curr-y/ecx: int <- copy 0
new-x, curr-y <- render-word-with-stack-and-cursor screen, line, curr-word, new-x, y, cursor-word
compare curr-y, new-y
{
break-if-<=
new-y <- copy curr-y
}
new-x <- add 1/inter-word-spacing
# update
var next-word-ah/eax: (addr handle word) <- get curr-word, next
var next-word/eax: (addr word) <- lookup *next-word-ah
curr-word <- copy next-word
loop
}
return new-x, new-y
}
fn render-word-with-stack-and-cursor screen: (addr screen), line: (addr line), curr-word: (addr word), x: int, y: int, _cursor-word-addr: int -> _/eax: int, _/ecx: int {
# print curr-word, with cursor if necessary
var render-cursor?/eax: boolean <- copy 0/false
var cursor-word-addr/ecx: int <- copy _cursor-word-addr
{
compare cursor-word-addr, curr-word
break-if-!=
render-cursor? <- copy 1/true
}
var new-x/eax: int <- render-word screen, curr-word, x, y, render-cursor?
add-to x, 1/word-stack-indent
var new-x-saved/edx: int <- copy new-x
add-to y, 2/word-stack-spacing
# compute stack until word
var stack-storage: value-stack
var stack/edi: (addr value-stack) <- address stack-storage
evaluate line, curr-word, stack
# render stack
var new-y/ecx: int <- copy 0
new-x, new-y <- render-value-stack screen, stack, x, y
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, new-x, 0xc/fg, 0/bg
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, new-y, 3/fg, 0/bg
compare new-x, new-x-saved
{
break-if->=
new-x <- copy new-x-saved
}
#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, new-x, 7/fg, 0/bg
return new-x, new-y
}
fn test-render-line-with-stack-singleton {
var line-storage: line
var line/esi: (addr line) <- address line-storage
parse-line "1", line
# setup: screen
var screen-on-stack: screen
var screen/edi: (addr screen) <- address screen-on-stack
initialize-screen screen, 0x20, 4
#
var new-x/eax: int <- copy 0
var new-y/ecx: int <- copy 0
new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
check-screen-row screen, 0/y, "1 ", "F - test-render-line-with-stack-singleton/0"
check-screen-row screen, 1/y, " ", "F - test-render-line-with-stack-singleton/1"
# ___
check-screen-row screen, 2/y, " 1 ", "F - test-render-line-with-stack-singleton/2"
check-screen-row screen, 3/y, " ", "F - test-render-line-with-stack-singleton/3"
# not bothering to test hash colors for numbers
}
fn test-render-line-with-stack {
var line-storage: line
var line/esi: (addr line) <- address line-storage
parse-line "1 2", line
# setup: screen
var screen-on-stack: screen
var screen/edi: (addr screen) <- address screen-on-stack
initialize-screen screen, 0x20, 8
#
var new-x/eax: int <- copy 0
var new-y/ecx: int <- copy 0
new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
check-screen-row screen, 0/y, "1 2 ", "F - test-render-line-with-stack/0"
check-screen-row screen, 1/y, " ", "F - test-render-line-with-stack/1"
# ___ ___
check-screen-row screen, 2/y, " 1 2 ", "F - test-render-line-with-stack/2"
check-screen-row screen, 3/y, " 1 ", "F - test-render-line-with-stack/3"
check-screen-row screen, 4/y, " ", "F - test-render-line-with-stack/4"
# not bothering to test hash colors for numbers
}
# { } groups have no effect on the stack by default.
fn test-render-line-with-stack-groups {
var line-storage: line
var line/esi: (addr line) <- address line-storage
parse-line "{ 1 2 }", line
# setup: screen
var screen-on-stack: screen
var screen/edi: (addr screen) <- address screen-on-stack
initialize-screen screen, 0x20, 8
#
var new-x/eax: int <- copy 0
var new-y/ecx: int <- copy 0
new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
check-screen-row screen, 0/y, "{ 1 2 } ", "F - test-render-line-with-stack-groups/0"
check-screen-row screen, 1/y, " ", "F - test-render-line-with-stack-groups/1"
# ___ ___
check-screen-row screen, 2/y, " 1 2 ", "F - test-render-line-with-stack-groups/2"
check-screen-row screen, 3/y, " 1 ", "F - test-render-line-with-stack-groups/3"
check-screen-row screen, 4/y, " ", "F - test-render-line-with-stack-groups/4"
}
# break skips rest of the containing group
#? fn test-render-line-with-break {
#? var line-storage: line
#? var line/esi: (addr line) <- address line-storage
#? parse-line "{ 1 break 2 }", line
#? # setup: screen
#? var screen-on-stack: screen
#? var screen/edi: (addr screen) <- address screen-on-stack
#? initialize-screen screen, 0x20, 8
#? #
#? var new-x/eax: int <- copy 0
#? var new-y/ecx: int <- copy 0
#? new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
#? check-screen-row screen, 0/y, "{ 1 break 2 } ", "F - test-render-line-with-break/0"
#? check-screen-row screen, 1/y, " ", "F - test-render-line-with-break/1"
#? # ___
#? check-screen-row screen, 2/y, " 1 ", "F - test-render-line-with-break/2"
#? #? check-screen-row screen, 3/y, " ", "F - test-render-line-with-break/3"
#? }
fn edit-line _self: (addr line), key: byte {
var self/esi: (addr line) <- copy _self
var cursor-word-ah/edx: (addr handle word) <- get self, cursor
var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
var cursor-word/ecx: (addr word) <- copy _cursor-word
compare key, 0x20/space
$edit-line:space: {
break-if-!=
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
return
}
# otherwise insert key within current word
var g/edx: grapheme <- copy key
add-grapheme-to-word cursor-word, g
# silently ignore other hotkeys
}
fn main {
var line-storage: line
var line/esi: (addr line) <- address line-storage
initialize-line line
{
clear-screen 0/screen
var dummy1/eax: int <- copy 0
var dummy2/ecx: int <- copy 0
dummy1, dummy2 <- render-line-with-stack 0/screen, line, 2/x, 2/y, 1/show-cursor
{
var key/eax: byte <- read-key 0/keyboard
compare key, 0
loop-if-=
edit-line line, key
}
loop
}
}

View File

@ -1,241 +0,0 @@
# value stacks encode the result of a program at a single point in time
# they are typically rendered vertically
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
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-int-to-value-stack _self: (addr value-stack), _val: int {
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 <- convert _val
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
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-boolean-to-value-stack _self: (addr value-stack), _val: boolean {
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 boolean) <- get dest-addr, boolean-data
var val/esi: boolean <- copy _val
copy-to *dest-addr2, val
increment *top-addr
var type-addr/eax: (addr int) <- get dest-addr, type
copy-to *type-addr, 3/boolean
}
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->
abort "pop number: empty stack"
}
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 pop-boolean-from-value-stack _self: (addr value-stack) -> _/eax: boolean {
var self/esi: (addr value-stack) <- copy _self
var top-addr/ecx: (addr int) <- get self, top
{
compare *top-addr, 0
break-if->
abort "pop boolean: empty stack"
}
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 boolean) <- get result-addr, boolean-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 test-boolean {
var stack-storage: value-stack
var stack/esi: (addr value-stack) <- address stack-storage
push-boolean-to-value-stack stack, 0/false
var result/eax: boolean <- pop-boolean-from-value-stack stack
check-not result, "F - test-boolean/false"
push-boolean-to-value-stack stack, 1/true
var result/eax: boolean <- pop-boolean-from-value-stack stack
check result, "F - test-boolean/true"
}
fn dump-stack _self: (addr value-stack) {
var self/esi: (addr value-stack) <- copy _self
var data-ah/eax: (addr handle array value) <- get self, data
var _data/eax: (addr array value) <- lookup *data-ah
var data/edi: (addr array value) <- copy _data
var top-addr/ecx: (addr int) <- get self, top
var top/ecx: int <- copy *top-addr
top <- decrement
var y/edx: int <- copy 0xa
var dummy/eax: int <- draw-text-rightward-over-full-screen 0/screen, "==", 0/x, 9/y, 0xc/red, 0/bg
{
compare top, 0
break-if-<
var dest-offset/eax: (offset value) <- compute-offset data, top
var curr/eax: (addr value) <- index data, dest-offset
var dummy/eax: int <- render-value 0/screen, curr, 0/x, y, 0/no-color
top <- decrement
y <- increment
loop
}
}
fn render-value-stack screen: (addr screen), _self: (addr value-stack), x: int, y: int -> _/eax: int, _/ecx: int {
var self/ecx: (addr value-stack) <- copy _self
var data-ah/eax: (addr handle array value) <- get self, data
var _data/eax: (addr array value) <- lookup *data-ah
var data/edi: (addr array value) <- copy _data
var top-addr/eax: (addr int) <- get self, top
var curr-idx/ecx: int <- copy *top-addr
curr-idx <- decrement
var new-x/edx: int <- copy 0
{
compare curr-idx, 0
break-if-<
var dest-offset/eax: (offset value) <- compute-offset data, curr-idx
var curr/eax: (addr value) <- index data, dest-offset
var curr-x/eax: int <- render-value screen, curr, x, y, 1/top-level
{
compare curr-x, new-x
break-if-<=
new-x <- copy curr-x
}
curr-idx <- decrement
increment y
loop
}
return new-x, y
}
fn test-render-value-stack {
var stack-storage: value-stack
var stack/esi: (addr value-stack) <- address stack-storage
push-int-to-value-stack stack, 3
# setup: screen
var screen-on-stack: screen
var screen/edi: (addr screen) <- address screen-on-stack
initialize-screen screen, 0x20, 4
#
var final-x/eax: int <- copy 0
var final-y/ecx: int <- copy 0
final-x, final-y <- render-value-stack screen, stack, 0/x, 0/y
check-ints-equal final-y, 1, "F - test-render-value-stack y"
check-ints-equal final-x, 3, "F - test-render-value-stack x"
}

View File

@ -1,718 +0,0 @@
type word {
scalar-data: (handle gap-buffer)
next: (handle word)
prev: (handle word)
}
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
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
{
compare self, 0
break-if-!=
return 0/false
}
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 words-equal? _self: (addr word), _w: (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 data/ecx: (addr gap-buffer) <- copy _data
var w/eax: (addr word) <- copy _w
var w-data-ah/eax: (addr handle gap-buffer) <- get w, scalar-data
var w-data/eax: (addr gap-buffer) <- lookup *w-data-ah
var result/eax: boolean <- gap-buffers-equal? data, w-data
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 skip-one-word _in: (addr handle word), out: (addr handle word) {
var in/eax: (addr handle word) <- copy _in
var curr/eax: (addr word) <- lookup *in
var next/eax: (addr handle word) <- get curr, next
copy-object next, out # modify 'out' right at the end, just in case it's same as 'in'
}
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 <- index-of-gap 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 render-word screen: (addr screen), _self: (addr word), x: int, y: int, render-cursor?: boolean -> _/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 <- render-gap-buffer screen, data, x, y, render-cursor?
return result
}
fn render-words screen: (addr screen), _words-ah: (addr handle word), x: int, y: int, cursor-word-addr: int -> _/eax: int {
var words-ah/eax: (addr handle word) <- copy _words-ah
var _words-a/eax: (addr word) <- lookup *words-ah
var words-a/ecx: (addr word) <- copy _words-a
compare words-a, 0
{
break-if-!=
return x
}
# print
var render-cursor?/edx: boolean <- copy 0/false
{
compare cursor-word-addr, words-a
break-if-!=
render-cursor? <- copy 1/true
}
var next-x/eax: int <- render-word screen, words-a, x, y, render-cursor?
var space/edx: grapheme <- copy 0x20/space
draw-grapheme screen, space, next-x, y, 3/fg=cyan, 0/bg
next-x <- increment
# recurse
var next-ah/ecx: (addr handle word) <- get words-a, next
next-x <- render-words screen, next-ah, next-x, y, cursor-word-addr
return next-x
}
fn test-render-words {
# words = [aaa, bbb, ccc, ddd]
var w-storage: (handle word)
var w-ah/esi: (addr handle word) <- address w-storage
allocate-word-with w-ah, "aaa"
append-word-at-end-with w-ah, "bbb"
append-word-at-end-with w-ah, "ccc"
append-word-at-end-with w-ah, "ddd"
# setup: screen
var screen-on-stack: screen
var screen/edi: (addr screen) <- address screen-on-stack
initialize-screen screen, 0x20, 4
#
var _w/eax: (addr word) <- lookup *w-ah
var w/ecx: (addr word) <- copy _w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words/0 cursor"
# - start moving cursor left through final word
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words/1 cursor"
#
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words/2 cursor"
#
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-words/3 cursor"
# further moves left within the word change nothing
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-words/4 cursor"
# - switch to next word
var w2-ah/eax: (addr handle word) <- get w, next
var _w/eax: (addr word) <- lookup *w2-ah
var w/ecx: (addr word) <- copy _w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words/5 cursor"
# now speed up a little
cursor-left w
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words/6 cursor"
#
var w2-ah/eax: (addr handle word) <- get w, next
var _w/eax: (addr word) <- lookup *w2-ah
var w/ecx: (addr word) <- copy _w
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "aaa bbb ccc ddd ", "F - test-render-words/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words/7 cursor"
}
fn render-words-in-reverse screen: (addr screen), _words-ah: (addr handle word), x: int, y: int, cursor-word-addr: int -> _/eax: int {
var words-ah/eax: (addr handle word) <- copy _words-ah
var _words-a/eax: (addr word) <- lookup *words-ah
var words-a/ecx: (addr word) <- copy _words-a
compare words-a, 0
{
break-if-!=
return x
}
# recurse
var next-ah/eax: (addr handle word) <- get words-a, next
var next-x/eax: int <- render-words-in-reverse screen, next-ah, x, y, cursor-word-addr
# print
var render-cursor?/edx: boolean <- copy 0/false
{
compare cursor-word-addr, words-a
break-if-!=
render-cursor? <- copy 1/true
}
next-x <- render-word screen, words-a, next-x, y, render-cursor?
var space/ecx: grapheme <- copy 0x20/space
draw-grapheme screen, space, next-x, y, 3/fg=cyan, 0/bg
next-x <- increment
return next-x
}
fn test-render-words-in-reverse {
# words = [aaa, bbb, ccc, ddd]
var w-storage: (handle word)
var w-ah/esi: (addr handle word) <- address w-storage
allocate-word-with w-ah, "aaa"
append-word-at-end-with w-ah, "bbb"
append-word-at-end-with w-ah, "ccc"
append-word-at-end-with w-ah, "ddd"
# setup: screen
var screen-on-stack: screen
var screen/edi: (addr screen) <- address screen-on-stack
initialize-screen screen, 0x20, 4
#
var _w/eax: (addr word) <- lookup *w-ah
var w/ecx: (addr word) <- copy _w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/0"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/0 cursor"
# - start moving cursor left through final word
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/1"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/1 cursor"
#
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/2"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/2 cursor"
#
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/3"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/3 cursor"
# further moves left within the word change nothing
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/4"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/4 cursor"
# - switch to next word
var w2-ah/eax: (addr handle word) <- get w, next
var _w/eax: (addr word) <- lookup *w2-ah
var w/ecx: (addr word) <- copy _w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/5"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/5 cursor"
# now speed up a little
cursor-left w
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/6"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/6 cursor"
#
var w2-ah/eax: (addr handle word) <- get w, next
var _w/eax: (addr word) <- lookup *w2-ah
var w/ecx: (addr word) <- copy _w
cursor-left w
var cursor-word/eax: int <- copy w
var new-x/eax: int <- render-words-in-reverse screen, w-ah, 0/x, 0/y, cursor-word
check-screen-row screen, 0/y, "ddd ccc bbb aaa ", "F - test-render-words-in-reverse/7"
check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-words-in-reverse/7 cursor"
}
# 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
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
# 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
# new->next = self->next
var src/esi: (addr handle word) <- get self, next
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-=
dest <- get next-addr, prev
copy-object new-ah, dest
}
# new->prev = saved-self
dest <- get new-addr, prev
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-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
}
fn word-exists? haystack: (addr word), needle: (addr word) -> _/eax: boolean {
# base case
compare haystack, 0
{
break-if-!=
return 0/false
}
# check current word
var found?/eax: boolean <- words-equal? haystack, needle
compare found?, 0/false
{
break-if-=
return 1/true
}
# recurse
var curr/eax: (addr word) <- copy haystack
var next-ah/eax: (addr handle word) <- get curr, next
var next/eax: (addr word) <- lookup *next-ah
var result/eax: boolean <- word-exists? next, needle
return result
}
fn test-word-exists? {
var needle-storage: word
var needle/esi: (addr word) <- address needle-storage
initialize-word-with needle, "abc"
var w-storage: (handle word)
var w-ah/edi: (addr handle word) <- address w-storage
allocate w-ah
var _w/eax: (addr word) <- lookup *w-ah
var w/ecx: (addr word) <- copy _w
initialize-word-with w, "aaa"
#
var result/eax: boolean <- word-exists? w, w
check result, "F - test-word-exists? reflexive"
result <- word-exists? w, needle
check-not result, "F - test-word-exists? 1"
append-word-at-end-with w-ah, "bbb"
result <- word-exists? w, needle
check-not result, "F - test-word-exists? 2"
append-word-at-end-with w-ah, "abc"
result <- word-exists? w, needle
check result, "F - test-word-exists? 3"
append-word-at-end-with w-ah, "ddd"
result <- word-exists? w, needle
check result, "F - test-word-exists? 4"
}
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
}
}