# Some primitives for checking the state of fake screen objects. # validate data on screen regardless of attributes (color, bold, etc.) # Mu doesn't have multi-line strings, so we provide functions for rows or portions of rows. # Tab characters (that translate into multiple screen cells) not supported. fn check-screen-row screen: (addr screen), y: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-from screen, 0/x, y, expected, msg } fn check-screen-row-from screen-on-stack: (addr screen), x: int, y: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var failure-count/edi: int <- copy 0 var idx/ecx: int <- screen-cell-index screen, x, y # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var expected-grapheme/eax: grapheme <- read-grapheme e-addr # compare graphemes $check-screen-row-from:compare-graphemes: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-from:compare-graphemes } # if (g == expected-grapheme) print "." compare g, expected-grapheme break-if-= # otherwise print an error failure-count <- increment draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") but observed '", 3/fg/cyan, 0/bg draw-grapheme-at-cursor 0/screen, g, 3/cyan, 0/bg move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "'", 3/fg/cyan, 0/bg move-cursor-to-left-margin-of-next-line 0/screen } idx <- increment increment x loop } # if any assertions failed, count the test as failed compare failure-count, 0 { break-if-= count-test-failure return } # otherwise print a "." draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg } # various variants by screen-cell attribute; spaces in the 'expected' data should not match the attribute fn check-screen-row-in-color screen: (addr screen), fg: int, y: int, expected: (addr array byte), msg: (addr array byte) { check-screen-row-in-color-from screen, fg, y, 0/x, expected, msg } fn check-screen-row-in-color-from screen-on-stack: (addr screen), fg: int, y: int, x: int, expected: (addr array byte), msg: (addr array byte) { var screen/esi: (addr screen) <- copy screen-on-stack var idx/ecx: int <- screen-cell-index screen, x, y # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme var e: (stream byte 0x100) var e-addr/edx: (addr stream byte) <- address e write e-addr, expected { var done?/eax: boolean <- stream-empty? e-addr compare done?, 0 break-if-!= var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx var g/ebx: grapheme <- copy _g var _expected-grapheme/eax: grapheme <- read-grapheme e-addr var expected-grapheme/edi: grapheme <- copy _expected-grapheme $check-screen-row-in-color-from:compare-cells: { # if expected-grapheme is space, null grapheme is also ok { compare expected-grapheme, 0x20 break-if-!= compare g, 0 break-if-= $check-screen-row-in-color-from:compare-cells } # if expected-grapheme is space, a different color is ok { compare expected-grapheme, 0x20 break-if-!= var color/eax: int <- screen-color-at-idx screen, idx compare color, fg break-if-!= $check-screen-row-in-color-from:compare-cells } # compare graphemes $check-screen-row-in-color-from:compare-graphemes: { # if (g == expected-grapheme) print "." compare g, expected-grapheme { break-if-!= draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg break $check-screen-row-in-color-from:compare-graphemes } # otherwise print an error count-test-failure draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg draw-text-wrapping-right-then-down-from-cursor-over-full-screen
//: So far you can have global variables by not setting default-space, and
//: local variables by setting default-space. You can isolate variables
//: between those extremes by creating 'surrounding' spaces.
//:
//: (Surrounding spaces are like lexical scopes in other languages.)

void test_surrounding_space() {
  run(
      // location 2 in space 1 (remember that locations 0 and 1 are reserved in all
      // spaces) refers to the space surrounding the default space, here 20.
      "def main [\n"
         // prepare default-space address
      "  10:num/alloc-id, 11:num <- copy 0, 1000\n"
         // prepare default-space payload
      "  1000:num <- copy 0\n"  // alloc id of payload
      "  1001:num <- copy 5\n"  // length
         // prepare address of chained space
      "  20:num/alloc-id, 21:num <- copy 0, 2000\n"
         // prepare payload of chained space
      "  2000:num <- copy 0\n"  // alloc id of payload
      "  2001:num <- copy 5\n"  // length
         // actual start of this recipe
      "  default-space:space <- copy 10:space\n"
         // later layers will explain the /names: property
      "  0:space/names:dummy <- copy 20:space/raw\n"
      "  2:num <- copy 94\n"
      "  2:num/space:1 <- copy 95\n"
      "]\n"
      "def dummy [\n"  // just for the /names: property above
      "]\n"
  );
  CHECK_TRACE_CONTENTS(
      // chain space: 1000 + (alloc id) 1 + (length) 1
      "mem: storing 0 in location 1002\n"
      "mem: storing 2000 in location 1003\n"
      // store to default space: 1000 + (alloc id) 1 + (length) 1 + (index) 2
      "mem: storing 94 in location 1004\n"
      // store to chained space: (contents of location 1003) 2000 + (alloc id) 1 + (length) 1 + (index) 2
      "mem: storing 95 in location 2004\n"
  );
}

//: If you think of a space as a collection of variables with a common
//: lifetime, surrounding allows managing shorter lifetimes inside a longer
//: one.

:(replace{} "int space_base(const reagent& x)")
int space_base(const reagent& x) {
  int base = current_call().default_space ? (current_call().default_space+/*skip alloc id*/1) : 0;
  return space_base(x, space_index(x), base);
}

int space_base(const reagent& x, int space_index, int base)