From bcb2190ec370b2009fd3ff6faf690758e4cd8e69 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Sat, 20 Feb 2021 21:51:33 -0800 Subject: 7758 Just some temporary files before I blow them away. --- baremetal/shell/eval.mu.debug | 1070 +++++++++++++++++++++++++++++++++++++++++ baremetal/shell/line.mu | 20 + 2 files changed, 1090 insertions(+) create mode 100644 baremetal/shell/eval.mu.debug diff --git a/baremetal/shell/eval.mu.debug b/baremetal/shell/eval.mu.debug new file mode 100644 index 00000000..2fcf6ec5 --- /dev/null +++ b/baremetal/shell/eval.mu.debug @@ -0,0 +1,1070 @@ +# 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 index 32b1422b..b67e3521 100644 --- a/baremetal/shell/line.mu +++ b/baremetal/shell/line.mu @@ -216,6 +216,26 @@ fn test-render-line-with-stack-groups { 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 -- cgit 1.4.1-2-gfad0