diff options
-rw-r--r-- | baremetal/shell/eval.mu | 1054 | ||||
-rw-r--r-- | baremetal/shell/eval.mu.debug | 1070 | ||||
-rw-r--r-- | baremetal/shell/line.mu | 275 | ||||
-rw-r--r-- | baremetal/shell/value-stack.mu | 241 | ||||
-rw-r--r-- | baremetal/shell/word.mu | 718 |
5 files changed, 0 insertions, 3358 deletions
diff --git a/baremetal/shell/eval.mu b/baremetal/shell/eval.mu deleted file mode 100644 index 0dc4aee9..00000000 --- a/baremetal/shell/eval.mu +++ /dev/null @@ -1,1054 +0,0 @@ -# evaluator (and parser) for the Mu shell language -# inputs: -# a list of lines, each a list of words, each an editable gap-buffer -# end: a word to stop at -# output: -# a stack of values to render that summarizes the result of evaluation until 'end' - -# Key features of the language: -# words matching '=___' create bindings -# line boundaries clear the stack (but not bindings) -# { and } for grouping words -# break and loop for control flow within groups -# -> for conditionally skipping the next word or group - -# Example: Pushing numbers from 1 to n on the stack -# -# 3 =n -# { n 1 < -> break n n 1- =n loop } -# -# Stack as we evaluate each word in the second line: -# 3 1 false 3 3 2 3 1 -# 3 3 3 2 -# 3 - -# Rules beyond simple postfix: -# If the final word is `->`, clear stack -# If the final word is `break`, pop top of stack -# -# `{` and `}` don't affect evaluation -# If the final word is `{` or `}`, clear stack (to suppress rendering it) -# -# If `->` in middle and top of stack is falsy, skip next word or group -# -# If `break` in middle executes, skip to next containing `}` -# If no containing `}`, clear stack (incomplete) -# -# If `loop` in middle executes, skip to previous containing `{` -# If no containing `}`, clear stack (error) - -fn evaluate _in: (addr line), end: (addr word), out: (addr value-stack) { - clear-value-stack out - var line/eax: (addr line) <- copy _in - var curr-ah/eax: (addr handle word) <- get line, data - var curr/eax: (addr word) <- lookup *curr-ah - evaluate-sub curr, end, out, 1/top-level -} - -fn evaluate-sub _curr: (addr word), end: (addr word), out: (addr value-stack), top-level?: boolean { - var curr/ecx: (addr word) <- copy _curr - var curr-stream-storage: (stream byte 0x10) - var curr-stream/edi: (addr stream byte) <- address curr-stream-storage - $evaluate-sub:loop: { - # safety net (should never hit) - compare curr, 0 - break-if-= - # pull next word in for parsing - emit-word curr, curr-stream -#? { -#? clear-screen 0/screen -#? dump-stack out -#? var foo/eax: int <- render-word 0/screen, curr, 0/x, 0/y, 0/no-cursor -#? { -#? var key/eax: byte <- read-key 0/keyboard -#? compare key, 0 -#? loop-if-= -#? } -#? } - $evaluate-sub:process-word: { - ### if curr-stream is an operator, perform it - { - var is-add?/eax: boolean <- stream-data-equal? curr-stream, "+" - compare is-add?, 0/false - 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-sub:process-word - } - { - var is-sub?/eax: boolean <- stream-data-equal? curr-stream, "-" - compare is-sub?, 0/false - 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-sub:process-word - } - { - var is-mul?/eax: boolean <- stream-data-equal? curr-stream, "*" - compare is-mul?, 0/false - 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-sub:process-word - } - { - var is-div?/eax: boolean <- stream-data-equal? curr-stream, "/" - compare is-div?, 0/false - 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-sub:process-word - } - { - var is-sqrt?/eax: boolean <- stream-data-equal? curr-stream, "sqrt" - compare is-sqrt?, 0/false - 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-sub:process-word - } - { - var is-lesser?/eax: boolean <- stream-data-equal? curr-stream, "<" - compare is-lesser?, 0/false - 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 - compare a, b - { - break-if-float< - push-boolean-to-value-stack out, 0/false - break $evaluate-sub:process-word - } - push-boolean-to-value-stack out, 1/true - break $evaluate-sub:process-word - } - { - var is-greater?/eax: boolean <- stream-data-equal? curr-stream, ">" - compare is-greater?, 0/false - 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 - compare a, b - { - break-if-float> - push-boolean-to-value-stack out, 0/false - break $evaluate-sub:process-word - } - push-boolean-to-value-stack out, 1/true - break $evaluate-sub:process-word - } - { - var is-equal?/eax: boolean <- stream-data-equal? curr-stream, "==" # TODO support non-numbers - compare is-equal?, 0/false - 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 - compare a, b - { - break-if-= - push-boolean-to-value-stack out, 0/false - break $evaluate-sub:process-word - } - push-boolean-to-value-stack out, 1/true - break $evaluate-sub:process-word - } - ## control flow - { - var is-conditional?/eax: boolean <- stream-data-equal? curr-stream, "->" - compare is-conditional?, 0/false - break-if-= - var a/eax: boolean <- pop-boolean-from-value-stack out - compare a, 0/false - { - break-if-!= - # if a is false, skip one word - var next-word: (handle word) - var next-word-ah/eax: (addr handle word) <- address next-word - skip-word curr, end, next-word-ah - var _curr/eax: (addr word) <- lookup *next-word-ah - curr <- copy _curr - } - break $evaluate-sub:process-word - } - { - var is-group-start?/eax: boolean <- stream-data-equal? curr-stream, "{" - compare is-group-start?, 0/false - break-if-= - # if top-level? and this is the final word, clear the stack - compare top-level?, 0/false - break-if-= $evaluate-sub:process-word - compare curr, end - break-if-!= $evaluate-sub:process-word - clear-value-stack out - break $evaluate-sub:process-word - } - { - var is-group-end?/eax: boolean <- stream-data-equal? curr-stream, "}" - compare is-group-end?, 0/false - break-if-= - # if top-level? and this is the final word, clear the stack - compare top-level?, 0/false - break-if-= $evaluate-sub:process-word - compare curr, end - break-if-!= $evaluate-sub:process-word - clear-value-stack out - break $evaluate-sub:process-word - } - { - var is-break?/eax: boolean <- stream-data-equal? curr-stream, "break" - compare is-break?, 0/false - break-if-= - # if curr == end, clear stack and break - # (TODO: move this into skip-rest-of-group) - compare curr, end - { - break-if-!= - clear-value-stack out - break $evaluate-sub:loop - } - # scan ahead to containing '}' - var next-word: (handle word) - var next-word-ah/eax: (addr handle word) <- address next-word - skip-rest-of-group curr, end, next-word-ah - var _curr/eax: (addr word) <- lookup *next-word-ah - curr <- copy _curr - # if '}' isn't found before end, we're rendering a word that isn't executed - # skip everything else and clear stack - var close-found?/eax: boolean <- word-equal? curr, "}" - compare close-found?, 0/false - { - break-if-!= - clear-value-stack out - } - loop $evaluate-sub:loop - } - { - var is-loop?/eax: boolean <- stream-data-equal? curr-stream, "loop" - compare is-loop?, 0/false - break-if-= - # scan back to containing '{' - var open-word: (handle word) - var open-word-ah/edx: (addr handle word) <- address open-word - scan-to-start-of-group curr, end, open-word-ah - # scan ahead to the containing '}'; record that as next word to eval at - var close-word: (handle word) - var close-word-ah/ebx: (addr handle word) <- address close-word - skip-rest-of-group curr, end, close-word-ah - var _curr/eax: (addr word) <- lookup *close-word-ah - curr <- copy _curr - # now eval until getting there - # TODO: can 'curr' be after 'end' at this point? - var open/eax: (addr word) <- lookup *open-word-ah - evaluate-sub open, curr, out, 0/nested - loop $evaluate-sub:loop - } - ## TEMPORARY 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/false - 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-sub:process-word - } - { - var is-swap?/eax: boolean <- stream-data-equal? curr-stream, "swap" - compare is-swap?, 0/false - 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-sub:process-word - } - ### if the word starts with a quote and ends with a quote, turn it into a string - { - rewind-stream curr-stream - 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-sub:process-word - } - ### if the word starts with a '[' and ends with a ']', turn it into an array - { - rewind-stream curr-stream - var start/eax: byte <- stream-first curr-stream - compare start, 0x5b/open-bracket - break-if-!= - var end/eax: byte <- stream-final curr-stream - compare end, 0x5d/close-bracket - 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-sub:process-word - } - ### otherwise assume it's a literal number and push it (can't parse floats yet) - { - 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 - var _curr/eax: (addr word) <- lookup *next-word-ah - curr <- copy _curr - # - loop - } -} - -fn skip-word _curr: (addr word), end: (addr word), out: (addr handle word) { - var curr/eax: (addr word) <- copy _curr - var bracket-count/ecx: int <- copy 0 - var result-ah/esi: (addr handle word) <- get curr, next - { - var result-val/eax: (addr word) <- lookup *result-ah - compare result-val, end - break-if-= - { - var open?/eax: boolean <- word-equal? result-val, "{" - compare open?, 0/false - break-if-= - bracket-count <- increment - } - { - var close?/eax: boolean <- word-equal? result-val, "}" - compare close?, 0/false - break-if-= - bracket-count <- decrement - compare bracket-count, 0 - { - break-if->= - abort "'->' cannot be final word in a {} group" # TODO: error-handling - } - } - compare bracket-count, 0 - break-if-= - result-ah <- get result-val, next - loop - } - copy-object result-ah, out -} - -# find next "}" from curr -# if you hit 'end' before "}", return null -fn skip-rest-of-group _curr: (addr word), end: (addr word), out: (addr handle word) { - var curr/eax: (addr word) <- copy _curr - var bracket-count/ecx: int <- copy 0 - var result-ah/esi: (addr handle word) <- get curr, next - $skip-rest-of-group:loop: { - var result-val/eax: (addr word) <- lookup *result-ah - compare result-val, 0 - break-if-= - { - var open?/eax: boolean <- word-equal? result-val, "{" - compare open?, 0/false - break-if-= - bracket-count <- increment - } - { - var close?/eax: boolean <- word-equal? result-val, "}" - compare close?, 0/false - break-if-= - compare bracket-count, 0 - break-if-= $skip-rest-of-group:loop - bracket-count <- decrement - } - compare result-val, end - { - break-if-!= - clear-object out - return - } - result-ah <- get result-val, next - loop - } - copy-object result-ah, out -} - -fn scan-to-start-of-group _curr: (addr word), end: (addr word), out: (addr handle word) { - var curr/eax: (addr word) <- copy _curr - var bracket-count/ecx: int <- copy 0 - var result-ah/esi: (addr handle word) <- get curr, prev - $scan-to-start-of-group:loop: { - var result-val/eax: (addr word) <- lookup *result-ah - compare result-val, 0 - break-if-= - compare result-val, end # not sure what error-detection should happen here - break-if-= - { - var open?/eax: boolean <- word-equal? result-val, "{" - compare open?, 0/false - break-if-= - compare bracket-count, 0 - break-if-= $scan-to-start-of-group:loop - bracket-count <- increment - } - { - var close?/eax: boolean <- word-equal? result-val, "}" - compare close?, 0/false - break-if-= - bracket-count <- decrement - } - result-ah <- get result-val, prev - loop - } - copy-object result-ah, out -} - -fn test-eval-arithmetic { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 1 +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-arithmetic stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 2, "F - test-eval-arithmetic result" -} - -fn test-eval-string { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "\"abc\"", in # TODO support spaces within strings - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-string stack size" - var out-data-ah/eax: (addr handle array value) <- get out, data - var out-data/eax: (addr array value) <- lookup *out-data-ah - var v/eax: (addr value) <- index out-data, 0 - var type/ecx: (addr int) <- get v, type - check-ints-equal *type, 1/text, "F - test-eval-string type" - var text-ah/eax: (addr handle array byte) <- get v, text-data - var text/eax: (addr array byte) <- lookup *text-ah - check-strings-equal text, "abc", "F - test-eval-string result" -} - -fn test-eval-compare-lesser { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 <", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-lesser stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check result, "F - test-eval-compare-lesser result" -} - -fn test-eval-compare-greater { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "2 1 >", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-greater stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check result, "F - test-eval-compare-greater result" -} - -fn test-eval-compare-equal-fails { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 ==", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-equal-fails stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check-not result, "F - test-eval-compare-equal-fails result" -} - -fn test-eval-compare-equal { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "2 2 ==", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-equal stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check result, "F - test-eval-compare-equal result" -} - -fn test-eval-conditional { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 < -> 3", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-conditional stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 3, "F - test-eval-conditional result" -} - -# if top of stack is false, `->` skips one word -fn test-eval-conditional-skipped { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 > -> 3", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-conditional-skipped stack size" -} - -# curlies have no effect in isolation -fn test-eval-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "{ 1 } 1 +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-group stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 2, "F - test-eval-group result" -} - -fn test-eval-group-open-at-end { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 1 + {", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-group-open-at-end stack size" -} - -fn test-eval-group-close-at-end { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "{ 1 1 + }", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-group-close-at-end stack size" -} - -fn test-eval-conditional-skips-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 > -> { 3 } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # out contains just the final sentinel '9' - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-conditional-skips-group stack size" -} - -fn test-eval-conditional-skips-nested-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 > -> { { 3 } 4 } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # out contains just the final sentinel '9' - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-conditional-skips-nested-group stack size" -} - -# TODO: test error-handling on: -# 1 2 > -> } - -# incomplete group rendering at 'break' -fn test-eval-break-incomplete { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # break clears stack when final word - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-break-incomplete stack size" -} - -# incomplete group rendering after 'break' -fn test-eval-break-incomplete-2 { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break 5", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # break clears stack when final word -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 0, "F - test-eval-break-incomplete-2 stack size" -} - -# complete group rendering at 'break' -fn test-eval-break-incomplete-3 { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "{ 3 break 4 } 5", in - # end = 'break' - var w-ah/edx: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - skip-one-word w-ah, end-ah - skip-one-word end-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # break clears stack when final word - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-break-incomplete-3 stack size" -} - -# { 1 break 2 } 3 => empty -# ^ -# -# { 1 break 2 } 3 => empty -# ^ -# -# { 1 break 2 } 3 => 1 3 -# ^ - -# break skips to next containing `}` -fn test-eval-break { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break 5 } +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # result is 3+4, not 4+5 - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-break stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 7, "F - test-eval-break result" -} - -fn test-eval-break-nested { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break { 5 } 6 } +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # result is 3+4, skipping remaining numbers - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-break-nested stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 7, "F - test-eval-break-nested result" -} - -#? 1 2 3 4 6 5 { < -> break loop } -#? 1 2 3 4 6 5 false 2 -#? 1 2 3 4 6 4 1 -#? 1 2 3 4 3 -#? 1 2 3 2 -#? 1 2 1 -#? 1 - -#? 1 2 3 4 { 3 == -> return loop } -#? 1 2 3 4 3 false 2 => 3 -#? 1 2 3 4 3 1 -#? 1 2 3 2 -#? 1 2 1 -#? 1 - -# loop skips to previous containing `{` and continues evaluating until control -# leaves the group -fn test-eval-loop { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 4 3 { < -> break loop } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 4 3 { < -> loop { < -> break 9 - # stack contents: 9 - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-loop stack size" -} - -fn test-eval-loop-2 { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 4 3 { 4 == -> break loop } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 4 3 { 4 == -> loop { 4 == -> break 9 - # stack contents: 1 2 9 -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 3, "F - test-eval-loop-2 stack size" -} - -fn test-eval-loop-conditional { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 3 { 3 == -> loop } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 3 { 3 == -> loop { 3 == -> 9 - # stack contents: 1 9 -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 2, "F - test-eval-loop-2-conditional stack size" -} - -fn test-eval-loop-with-words-after-in-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 3 { 3 == -> loop 37 } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 3 { 3 == -> loop { 3 == -> 37 } 9 - # stack contents: 1 37 9 -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 3, "F - test-eval-loop-with-words-after-in-group stack size" -} diff --git a/baremetal/shell/eval.mu.debug b/baremetal/shell/eval.mu.debug deleted file mode 100644 index 2fcf6ec5..00000000 --- a/baremetal/shell/eval.mu.debug +++ /dev/null @@ -1,1070 +0,0 @@ -# evaluator (and parser) for the Mu shell language -# inputs: -# a list of lines, each a list of words, each an editable gap-buffer -# end: a word to stop at -# output: -# a stack of values to render that summarizes the result of evaluation until 'end' - -# Key features of the language: -# words matching '=___' create bindings -# line boundaries clear the stack (but not bindings) -# { and } for grouping words -# break and loop for control flow within groups -# -> for conditionally skipping the next word or group - -# Example: Pushing numbers from 1 to n on the stack -# -# 3 =n -# { n 1 < -> break n n 1- =n loop } -# -# Stack as we evaluate each word in the second line: -# 3 1 false 3 3 2 3 1 -# 3 3 3 2 -# 3 - -# Rules beyond simple postfix: -# If the final word is `->`, clear stack -# If the final word is `break`, pop top of stack -# -# `{` and `}` don't affect evaluation -# If the final word is `{` or `}`, clear stack (to suppress rendering it) -# -# If `->` in middle and top of stack is falsy, skip next word or group -# -# If `break` in middle executes, skip to next containing `}` -# If no containing `}`, clear stack (incomplete) -# -# If `loop` in middle executes, skip to previous containing `{` -# If no containing `}`, clear stack (error) - -fn evaluate _in: (addr line), end: (addr word), out: (addr value-stack) { - clear-value-stack out - var line/eax: (addr line) <- copy _in - var curr-ah/eax: (addr handle word) <- get line, data - var curr/eax: (addr word) <- lookup *curr-ah - evaluate-sub curr, end, out, 1/top-level -} - -fn evaluate-sub _curr: (addr word), end: (addr word), out: (addr value-stack), top-level?: boolean { - var curr/ecx: (addr word) <- copy _curr - var curr-stream-storage: (stream byte 0x10) - var curr-stream/edi: (addr stream byte) <- address curr-stream-storage - $evaluate-sub:loop: { - # safety net (should never hit) - compare curr, 0 - break-if-= - # pull next word in for parsing - emit-word curr, curr-stream -#? { -#? clear-screen 0/screen -#? dump-stack out -#? var foo/eax: int <- render-word 0/screen, curr, 0/x, 0/y, 0/no-cursor -#? { -#? var key/eax: byte <- read-key 0/keyboard -#? compare key, 0 -#? loop-if-= -#? } -#? } - $evaluate-sub:process-word: { - ### if curr-stream is an operator, perform it - { - var is-add?/eax: boolean <- stream-data-equal? curr-stream, "+" - compare is-add?, 0/false - 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-sub:process-word - } - { - var is-sub?/eax: boolean <- stream-data-equal? curr-stream, "-" - compare is-sub?, 0/false - 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-sub:process-word - } - { - var is-mul?/eax: boolean <- stream-data-equal? curr-stream, "*" - compare is-mul?, 0/false - 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-sub:process-word - } - { - var is-div?/eax: boolean <- stream-data-equal? curr-stream, "/" - compare is-div?, 0/false - 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-sub:process-word - } - { - var is-sqrt?/eax: boolean <- stream-data-equal? curr-stream, "sqrt" - compare is-sqrt?, 0/false - 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-sub:process-word - } - { - var is-lesser?/eax: boolean <- stream-data-equal? curr-stream, "<" - compare is-lesser?, 0/false - 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 - compare a, b - { - break-if-float< - push-boolean-to-value-stack out, 0/false - break $evaluate-sub:process-word - } - push-boolean-to-value-stack out, 1/true - break $evaluate-sub:process-word - } - { - var is-greater?/eax: boolean <- stream-data-equal? curr-stream, ">" - compare is-greater?, 0/false - 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 - compare a, b - { - break-if-float> - push-boolean-to-value-stack out, 0/false - break $evaluate-sub:process-word - } - push-boolean-to-value-stack out, 1/true - break $evaluate-sub:process-word - } - { - var is-equal?/eax: boolean <- stream-data-equal? curr-stream, "==" # TODO support non-numbers - compare is-equal?, 0/false - 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 - compare a, b - { - break-if-= - push-boolean-to-value-stack out, 0/false - break $evaluate-sub:process-word - } - push-boolean-to-value-stack out, 1/true - break $evaluate-sub:process-word - } - ## control flow - { - var is-conditional?/eax: boolean <- stream-data-equal? curr-stream, "->" - compare is-conditional?, 0/false - break-if-= - var a/eax: boolean <- pop-boolean-from-value-stack out - compare a, 0/false - { - break-if-!= - # if a is false, skip one word - var next-word: (handle word) - var next-word-ah/eax: (addr handle word) <- address next-word - skip-word curr, end, next-word-ah - var _curr/eax: (addr word) <- lookup *next-word-ah - curr <- copy _curr - } - break $evaluate-sub:process-word - } - { - var is-group-start?/eax: boolean <- stream-data-equal? curr-stream, "{" - compare is-group-start?, 0/false - break-if-= - # if top-level? and this is the final word, clear the stack - compare top-level?, 0/false - break-if-= $evaluate-sub:process-word - compare curr, end - break-if-!= $evaluate-sub:process-word - clear-value-stack out - break $evaluate-sub:process-word - } - { - var is-group-end?/eax: boolean <- stream-data-equal? curr-stream, "}" - compare is-group-end?, 0/false - break-if-= - # if top-level? and this is the final word, clear the stack - compare top-level?, 0/false - break-if-= $evaluate-sub:process-word - compare curr, end - break-if-!= $evaluate-sub:process-word - clear-value-stack out - break $evaluate-sub:process-word - } - { - var is-break?/eax: boolean <- stream-data-equal? curr-stream, "break" - compare is-break?, 0/false - break-if-= - # if curr == end, clear stack and break - compare curr, end - { - break-if-!= - clear-value-stack out - break $evaluate-sub:loop - } - # scan ahead to containing '}' - # if it isn't found before end, skip everything else - var next-word: (handle word) - var next-word-ah/eax: (addr handle word) <- address next-word - skip-rest-of-group curr, end, next-word-ah -#? { -#? var dummy/eax: int <- render-words 0/screen, next-word-ah, 0/x, 5/y, 0/no-cursor -#? } - var _curr/eax: (addr word) <- lookup *next-word-ah - curr <- copy _curr - var close-found?/eax: boolean <- word-equal? curr, "}" - compare close-found?, 0/false - { - break-if-!= - clear-value-stack out - } - loop $evaluate-sub:loop - } - { - var is-loop?/eax: boolean <- stream-data-equal? curr-stream, "loop" - compare is-loop?, 0/false - break-if-= - # scan back to containing '{' - var open-word: (handle word) - var open-word-ah/edx: (addr handle word) <- address open-word - scan-to-start-of-group curr, end, open-word-ah - # scan ahead to the containing '}'; record that as next word to eval at - var close-word: (handle word) - var close-word-ah/ebx: (addr handle word) <- address close-word - skip-rest-of-group curr, end, close-word-ah - var _curr/eax: (addr word) <- lookup *close-word-ah - curr <- copy _curr - # now eval until getting there - # TODO: can 'curr' be after 'end' at this point? - var open/eax: (addr word) <- lookup *open-word-ah - evaluate-sub open, curr, out, 0/nested - loop $evaluate-sub:loop - } - ## TEMPORARY 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/false - 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-sub:process-word - } - { - var is-swap?/eax: boolean <- stream-data-equal? curr-stream, "swap" - compare is-swap?, 0/false - 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-sub:process-word - } - ### if the word starts with a quote and ends with a quote, turn it into a string - { - rewind-stream curr-stream - 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-sub:process-word - } - ### if the word starts with a '[' and ends with a ']', turn it into an array - { - rewind-stream curr-stream - var start/eax: byte <- stream-first curr-stream - compare start, 0x5b/open-bracket - break-if-!= - var end/eax: byte <- stream-final curr-stream - compare end, 0x5d/close-bracket - 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-sub:process-word - } - ### otherwise assume it's a literal number and push it (can't parse floats yet) - { - 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 - var _curr/eax: (addr word) <- lookup *next-word-ah - curr <- copy _curr - # - loop - } -} - -fn skip-word _curr: (addr word), end: (addr word), out: (addr handle word) { - var curr/eax: (addr word) <- copy _curr - var bracket-count/ecx: int <- copy 0 - var result-ah/esi: (addr handle word) <- get curr, next - { - var result-val/eax: (addr word) <- lookup *result-ah - compare result-val, end - break-if-= - { - var open?/eax: boolean <- word-equal? result-val, "{" - compare open?, 0/false - break-if-= - bracket-count <- increment - } - { - var close?/eax: boolean <- word-equal? result-val, "}" - compare close?, 0/false - break-if-= - bracket-count <- decrement - compare bracket-count, 0 - { - break-if->= - abort "'->' cannot be final word in a {} group" # TODO: error-handling - } - } - compare bracket-count, 0 - break-if-= - result-ah <- get result-val, next - loop - } - copy-object result-ah, out -} - -# find next '}' from curr -# if you hit 'end' first, return null -fn skip-rest-of-group _curr: (addr word), end: (addr word), out: (addr handle word) { - var curr/eax: (addr word) <- copy _curr - var bracket-count/ecx: int <- copy 0 - var y/edx: int <- copy 0x18 - var result-ah/esi: (addr handle word) <- get curr, next - $skip-rest-of-group:loop: { - var result-val/eax: (addr word) <- lookup *result-ah - compare result-val, 0 - break-if-= -#? { -#? var dummy/eax: int <- render-word 0/screen, result-val, 0/x, y, 0/false -#? var dummy/eax: byte <- read-key 0/keyboard -#? y <- increment -#? } - compare result-val, end - { - break-if-!= -#? { -#? var d1/eax: int <- copy 0 -#? var d2/ecx: int <- copy 0 -#? d1, d2 <- draw-text-wrapping-right-then-down-over-full-screen 0/screen, "clearing", 0/x, 0x10/y, 6/fg, 0/bg -#? } - clear-object out - return - } - { - var open?/eax: boolean <- word-equal? result-val, "{" - compare open?, 0/false - break-if-= - bracket-count <- increment - } - { - var close?/eax: boolean <- word-equal? result-val, "}" - compare close?, 0/false - break-if-= - compare bracket-count, 0 - break-if-= $skip-rest-of-group:loop - bracket-count <- decrement - } - result-ah <- get result-val, next - loop - } - copy-object result-ah, out -} - -fn scan-to-start-of-group _curr: (addr word), end: (addr word), out: (addr handle word) { - var curr/eax: (addr word) <- copy _curr - var bracket-count/ecx: int <- copy 0 - var result-ah/esi: (addr handle word) <- get curr, prev - $scan-to-start-of-group:loop: { - var result-val/eax: (addr word) <- lookup *result-ah - compare result-val, 0 - break-if-= - compare result-val, end # not sure what error-detection should happen here - break-if-= - { - var open?/eax: boolean <- word-equal? result-val, "{" - compare open?, 0/false - break-if-= - compare bracket-count, 0 - break-if-= $scan-to-start-of-group:loop - bracket-count <- increment - } - { - var close?/eax: boolean <- word-equal? result-val, "}" - compare close?, 0/false - break-if-= - bracket-count <- decrement - } - result-ah <- get result-val, prev - loop - } - copy-object result-ah, out -} - -fn test-eval-arithmetic { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 1 +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-arithmetic stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 2, "F - test-eval-arithmetic result" -} - -fn test-eval-string { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "\"abc\"", in # TODO support spaces within strings - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-string stack size" - var out-data-ah/eax: (addr handle array value) <- get out, data - var out-data/eax: (addr array value) <- lookup *out-data-ah - var v/eax: (addr value) <- index out-data, 0 - var type/ecx: (addr int) <- get v, type - check-ints-equal *type, 1/text, "F - test-eval-string type" - var text-ah/eax: (addr handle array byte) <- get v, text-data - var text/eax: (addr array byte) <- lookup *text-ah - check-strings-equal text, "abc", "F - test-eval-string result" -} - -fn test-eval-compare-lesser { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 <", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-lesser stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check result, "F - test-eval-compare-lesser result" -} - -fn test-eval-compare-greater { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "2 1 >", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-greater stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check result, "F - test-eval-compare-greater result" -} - -fn test-eval-compare-equal-fails { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 ==", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-equal-fails stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check-not result, "F - test-eval-compare-equal-fails result" -} - -fn test-eval-compare-equal { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "2 2 ==", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-compare-equal stack size" - var result/eax: boolean <- pop-boolean-from-value-stack out - check result, "F - test-eval-compare-equal result" -} - -fn test-eval-conditional { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 < -> 3", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-conditional stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 3, "F - test-eval-conditional result" -} - -# if top of stack is false, `->` skips one word -fn test-eval-conditional-skipped { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 > -> 3", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-conditional-skipped stack size" -} - -# curlies have no effect in isolation -fn test-eval-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "{ 1 } 1 +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-group stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 2, "F - test-eval-group result" -} - -fn test-eval-group-open-at-end { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 1 + {", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-group-open-at-end stack size" -} - -fn test-eval-group-close-at-end { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "{ 1 1 + }", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-group-close-at-end stack size" -} - -fn test-eval-conditional-skips-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 > -> { 3 } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # out contains just the final sentinel '9' - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-conditional-skips-group stack size" -} - -fn test-eval-conditional-skips-nested-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 > -> { { 3 } 4 } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # out contains just the final sentinel '9' - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-conditional-skips-nested-group stack size" -} - -# TODO: test error-handling on: -# 1 2 > -> } - -# incomplete group rendering at 'break' -fn test-eval-break-incomplete { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # break clears stack when final word - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-break-incomplete stack size" -} - -# incomplete group rendering after 'break' -fn test-eval-break-incomplete-2 { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break 5", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # break clears stack when final word - var len/eax: int <- value-stack-length out - check-ints-equal len, 0, "F - test-eval-break-incomplete-2 stack size" -} - -# complete group rendering at 'break' -fn test-eval-break-incomplete-3 { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "{ 3 break 4 } 5", in - # end = 'break' - var w-ah/edx: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - skip-one-word w-ah, end-ah - skip-one-word end-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # -#? { -#? var dummy/eax: int <- render-words 0/screen, w-ah, 0/x, 0/y, 0/no-cursor -#? var dummy/eax: int <- render-words 0/screen, end-ah, 0/x, 1/y, 0/no-cursor -#? } - evaluate in, end, out -#? dump-stack out - # break clears stack when final word - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 0, "F - test-eval-break-incomplete-3 stack size" -} - -# { 1 break 2 } 3 => empty -# ^ -# -# { 1 break 2 } 3 => empty -# ^ -# -# { 1 break 2 } 3 => 1 3 -# ^ - -# break skips to next containing `}` -fn test-eval-break { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break 5 } +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # result is 3+4, not 4+5 - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-break stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 7, "F - test-eval-break result" -} - -fn test-eval-break-nested { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "3 { 4 break { 5 } 6 } +", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # result is 3+4, skipping remaining numbers - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-break-nested stack size" - var n/xmm0: float <- pop-number-from-value-stack out - var n2/eax: int <- convert n - check-ints-equal n2, 7, "F - test-eval-break-nested result" -} - -#? 1 2 3 4 6 5 { < -> break loop } -#? 1 2 3 4 6 5 false 2 -#? 1 2 3 4 6 4 1 -#? 1 2 3 4 3 -#? 1 2 3 2 -#? 1 2 1 -#? 1 - -#? 1 2 3 4 { 3 == -> return loop } -#? 1 2 3 4 3 false 2 => 3 -#? 1 2 3 4 3 1 -#? 1 2 3 2 -#? 1 2 1 -#? 1 - -# loop skips to previous containing `{` and continues evaluating until control -# leaves the group -fn test-eval-loop { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 4 3 { < -> break loop } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 4 3 { < -> loop { < -> break 9 - # stack contents: 9 - var len/eax: int <- value-stack-length out - check-ints-equal len, 1, "F - test-eval-loop stack size" -} - -fn test-eval-loop-2 { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 4 3 { 4 == -> break loop } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 4 3 { 4 == -> loop { 4 == -> break 9 - # stack contents: 1 2 9 -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 3, "F - test-eval-loop-2 stack size" -} - -fn test-eval-loop-conditional { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 3 { 3 == -> loop } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 3 { 3 == -> loop { 3 == -> 9 - # stack contents: 1 9 -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 2, "F - test-eval-loop-2-conditional stack size" -} - -fn test-eval-loop-with-words-after-in-group { - # in - var in-storage: line - var in/esi: (addr line) <- address in-storage - parse-line "1 2 3 { 3 == -> loop 37 } 9", in - # end - var w-ah/eax: (addr handle word) <- get in, data - var end-h: (handle word) - var end-ah/ecx: (addr handle word) <- address end-h - final-word w-ah, end-ah - var end/eax: (addr word) <- lookup *end-ah - # out - var out-storage: value-stack - var out/edi: (addr value-stack) <- address out-storage - initialize-value-stack out, 8 - # - evaluate in, end, out - # evaluation order: 1 2 3 { 3 == -> loop { 3 == -> 37 } 9 - # stack contents: 1 37 9 -#? dump-stack out - var len/eax: int <- value-stack-length out -#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, len, 0xc/red, 0/black - check-ints-equal len, 3, "F - test-eval-loop-with-words-after-in-group stack size" -} diff --git a/baremetal/shell/line.mu b/baremetal/shell/line.mu deleted file mode 100644 index b67e3521..00000000 --- a/baremetal/shell/line.mu +++ /dev/null @@ -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 - } -} diff --git a/baremetal/shell/value-stack.mu b/baremetal/shell/value-stack.mu deleted file mode 100644 index 8c9c2f47..00000000 --- a/baremetal/shell/value-stack.mu +++ /dev/null @@ -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" -} diff --git a/baremetal/shell/word.mu b/baremetal/shell/word.mu deleted file mode 100644 index ad57d040..00000000 --- a/baremetal/shell/word.mu +++ /dev/null @@ -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 - } -} |