about summary refs log tree commit diff stats
path: root/032array.cc
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-03-12 18:56:55 -0700
committerKartik Agaram <vc@akkartik.com>2019-03-12 19:14:12 -0700
commit4a943d4ed313eff001504c2b5c472266e86a38af (patch)
treea5757233a8c81b303a808f251180c7344071ed51 /032array.cc
parent43711b0e9f18e0225ce14687fb6ea0902aa6fc61 (diff)
downloadmu-4a943d4ed313eff001504c2b5c472266e86a38af.tar.gz
5001 - drop the :(scenario) DSL
I've been saying for a while[1][2][3] that adding extra abstractions makes
things harder for newcomers, and adding new notations doubly so. And then
I notice this DSL in my own backyard. Makes me feel like a hypocrite.

[1] https://news.ycombinator.com/item?id=13565743#13570092
[2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning
[3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2

The implementation of the DSL was also highly hacky:

a) It was happening in the tangle/ tool, but was utterly unrelated to tangling
layers.

b) There were several persnickety constraints on the different kinds of
lines and the specific order they were expected in. I kept finding bugs
where the translator would silently do the wrong thing. Or the error messages
sucked, and readers may be stuck looking at the generated code to figure
out what happened. Fixing error messages would require a lot more code,
which is one of my arguments against DSLs in the first place: they may
be easy to implement, but they're hard to design to go with the grain of
the underlying platform. They require lots of iteration. Is that effort
worth prioritizing in this project?

On the other hand, the DSL did make at least some readers' life easier,
the ones who weren't immediately put off by having to learn a strange syntax.
There were fewer quotes to parse, fewer backslash escapes.

Anyway, since there are also people who dislike having to put up with strange
syntaxes, we'll call that consideration a wash and tear this DSL out.

---

This commit was sheer drudgery. Hopefully it won't need to be redone with
a new DSL because I grow sick of backslashes.
Diffstat (limited to '032array.cc')
-rw-r--r--032array.cc451
1 files changed, 274 insertions, 177 deletions
diff --git a/032array.cc b/032array.cc
index 7f36411f..30adde20 100644
--- a/032array.cc
+++ b/032array.cc
@@ -6,12 +6,17 @@
 //: Create containers containing addresses to arrays instead.
 
 //: You can create arrays using 'create-array'.
-:(scenario create_array)
-def main [
-  # create an array occupying locations 1 (for the size) and 2-4 (for the elements)
-  1:array:num:3 <- create-array
-]
-+run: creating array from 4 locations
+void test_create_array() {
+  run(
+      "def main [\n"
+      // create an array occupying locations 1 (for the size) and 2-4 (for the elements)
+      "  1:array:num:3 <- create-array\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: creating array from 4 locations\n"
+  );
+}
 
 :(before "End Primitive Recipe Declarations")
 CREATE_ARRAY,
@@ -69,34 +74,45 @@ case CREATE_ARRAY: {
   break;
 }
 
-:(scenario copy_array)
-# Arrays can be copied around with a single instruction just like numbers,
-# no matter how large they are.
-# You don't need to pass the size around, since each array variable stores its
-# size in memory at run-time. We'll call a variable with an explicit size a
-# 'static' array, and one without a 'dynamic' array since it can contain
-# arrays of many different sizes.
-def main [
-  1:array:num:3 <- create-array
-  2:num <- copy 14
-  3:num <- copy 15
-  4:num <- copy 16
-  5:array:num <- copy 1:array:num:3
-]
-+mem: storing 3 in location 5
-+mem: storing 14 in location 6
-+mem: storing 15 in location 7
-+mem: storing 16 in location 8
-
-:(scenario stash_array)
-def main [
-  1:array:num:3 <- create-array
-  2:num <- copy 14
-  3:num <- copy 15
-  4:num <- copy 16
-  stash [foo:], 1:array:num:3
-]
-+app: foo: 3 14 15 16
+:(code)
+// Arrays can be copied around with a single instruction just like numbers,
+// no matter how large they are.
+// You don't need to pass the size around, since each array variable stores its
+// size in memory at run-time. We'll call a variable with an explicit size a
+// 'static' array, and one without a 'dynamic' array since it can contain
+// arrays of many different sizes.
+void test_copy_array() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  2:num <- copy 14\n"
+      "  3:num <- copy 15\n"
+      "  4:num <- copy 16\n"
+      "  5:array:num <- copy 1:array:num:3\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 3 in location 5\n"
+      "mem: storing 14 in location 6\n"
+      "mem: storing 15 in location 7\n"
+      "mem: storing 16 in location 8\n"
+  );
+}
+
+void test_stash_array() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  2:num <- copy 14\n"
+      "  3:num <- copy 15\n"
+      "  4:num <- copy 16\n"
+      "  stash [foo:], 1:array:num:3\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "app: foo: 3 14 15 16\n"
+  );
+}
 
 :(before "End types_coercible Special-cases")
 if (is_mu_array(from) && is_mu_array(to))
@@ -133,23 +149,33 @@ if (x.type && !x.type->atom && x.type->left->value == Array_type_ordinal) return
 //: arrays are disallowed inside containers unless their length is fixed in
 //: advance
 
-:(scenario container_permits_static_array_element)
-container foo [
-  x:array:num:3
-]
-$error: 0
+:(code)
+void test_container_permits_static_array_element() {
+  run(
+      "container foo [\n"
+      "  x:array:num:3\n"
+      "]\n"
+  );
+  CHECK_TRACE_COUNT("error", 0);
+}
 
 :(before "End insert_container Special-cases")
 else if (is_integer(type->name)) {  // sometimes types will contain non-type tags, like numbers for the size of an array
   type->value = 0;
 }
 
-:(scenario container_disallows_dynamic_array_element)
-% Hide_errors = true;
-container foo [
-  x:array:num
-]
-+error: container 'foo' cannot determine size of element 'x'
+:(code)
+void test_container_disallows_dynamic_array_element() {
+  Hide_errors = true;
+  run(
+      "container foo [\n"
+      "  x:array:num\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: container 'foo' cannot determine size of element 'x'\n"
+  );
+}
 
 :(before "End Load Container Element Definition")
 {
@@ -178,64 +204,86 @@ if (current_call().running_step_index < SIZE(get(Recipe, current_call().running_
   return false;
 }
 
-:(scenario merge_static_array_into_container)
-container foo [
-  x:num
-  y:array:num:3
-]
-def main [
-  1:array:num:3 <- create-array
-  10:foo <- merge 34, 1:array:num:3
-]
-# no errors
-
-:(scenario code_inside_container)
-% Hide_errors = true;
-container card [
-  rank:num <- next-ingredient
-]
-def foo [
-  1:card <- merge 3
-  2:num <- get 1:card rank:offset
-]
-# shouldn't die
+:(code)
+void test_merge_static_array_into_container() {
+  run(
+      "container foo [\n"
+      "  x:num\n"
+      "  y:array:num:3\n"
+      "]\n"
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  10:foo <- merge 34, 1:array:num:3\n"
+      "]\n"
+  );
+  // no errors
+}
+
+void test_code_inside_container() {
+  Hide_errors = true;
+  run(
+      "container card [\n"
+      "  rank:num <- next-ingredient\n"
+      "]\n"
+      "def foo [\n"
+      "  1:card <- merge 3\n"
+      "  2:num <- get 1:card rank:offset\n"
+      "]\n"
+  );
+  // shouldn't die
+}
 
 //:: To access elements of an array, use 'index'
 
-:(scenario index)
-def main [
-  1:array:num:3 <- create-array
-  2:num <- copy 14
-  3:num <- copy 15
-  4:num <- copy 16
-  10:num <- index 1:array:num:3, 0/index  # the index must be a non-negative whole number
-]
-+mem: storing 14 in location 10
-
-:(scenario index_compound_element)
-def main [
-  {1: (array (address number) 3)} <- create-array
-  # skip alloc id
-  3:num <- copy 14
-  # skip alloc id
-  5:num <- copy 15
-  # skip alloc id
-  7:num <- copy 16
-  10:address:num <- index {1: (array (address number) 3)}, 0
-]
-# skip alloc id
-+mem: storing 14 in location 11
-
-:(scenario index_direct_offset)
-def main [
-  1:array:num:3 <- create-array
-  2:num <- copy 14
-  3:num <- copy 15
-  4:num <- copy 16
-  10:num <- copy 0
-  20:num <- index 1:array:num, 10:num
-]
-+mem: storing 14 in location 20
+void test_index() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  2:num <- copy 14\n"
+      "  3:num <- copy 15\n"
+      "  4:num <- copy 16\n"
+      "  10:num <- index 1:array:num:3, 0/index\n"  // the index must be a non-negative whole number
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 14 in location 10\n"
+  );
+}
+
+void test_index_compound_element() {
+  run(
+      "def main [\n"
+      "  {1: (array (address number) 3)} <- create-array\n"
+      // skip alloc id
+      "  3:num <- copy 14\n"
+      // skip alloc id
+      "  5:num <- copy 15\n"
+      // skip alloc id
+      "  7:num <- copy 16\n"
+      "  10:address:num <- index {1: (array (address number) 3)}, 0\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      // skip alloc id
+      "mem: storing 14 in location 11\n"
+  );
+}
+
+void test_index_direct_offset() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  2:num <- copy 14\n"
+      "  3:num <- copy 15\n"
+      "  4:num <- copy 16\n"
+      "  10:num <- copy 0\n"
+      "  20:num <- index 1:array:num, 10:num\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 14 in location 20\n"
+  );
+}
 
 :(before "End Primitive Recipe Declarations")
 INDEX,
@@ -344,58 +392,86 @@ void test_array_length_static() {
   CHECK_EQ(array_length(x), 3);
 }
 
-:(scenario index_truncates)
-def main [
-  1:array:num:3 <- create-array
-  2:num <- copy 14
-  3:num <- copy 15
-  4:num <- copy 16
-  10:num <- index 1:array:num:3, 1.5  # non-whole number
-]
-# fraction is truncated away
-+mem: storing 15 in location 10
-
-:(scenario index_out_of_bounds)
-% Hide_errors = true;
-def main [
-  1:array:point:3 <- create-array
-  index 1:array:point:3, 4  # less than size of array in locations, but larger than its length in elements
-]
-+error: main: invalid index 4 in 'index 1:array:point:3, 4'
-
-:(scenario index_out_of_bounds_2)
-% Hide_errors = true;
-def main [
-  1:array:num:3 <- create-array
-  index 1:array:num, -1
-]
-+error: main: invalid index -1 in 'index 1:array:num, -1'
-
-:(scenario index_product_type_mismatch)
-% Hide_errors = true;
-def main [
-  1:array:point:3 <- create-array
-  10:num <- index 1:array:point, 0
-]
-+error: main: 'index' on '1:array:point' can't be saved in '10:num'; type should be 'point'
+void test_index_truncates() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  2:num <- copy 14\n"
+      "  3:num <- copy 15\n"
+      "  4:num <- copy 16\n"
+      "  10:num <- index 1:array:num:3, 1.5\n"  // non-whole number
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      // fraction is truncated away
+      "mem: storing 15 in location 10\n"
+  );
+}
+
+void test_index_out_of_bounds() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:array:point:3 <- create-array\n"
+      "  index 1:array:point:3, 4\n"  // less than size of array in locations, but larger than its length in elements
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: invalid index 4 in 'index 1:array:point:3, 4'\n"
+  );
+}
+
+void test_index_out_of_bounds_2() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  index 1:array:num, -1\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: invalid index -1 in 'index 1:array:num, -1'\n"
+  );
+}
+
+void test_index_product_type_mismatch() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:array:point:3 <- create-array\n"
+      "  10:num <- index 1:array:point, 0\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: 'index' on '1:array:point' can't be saved in '10:num'; type should be 'point'\n"
+  );
+}
 
 //: we might want to call 'index' without saving the results, say in a sandbox
 
-:(scenario index_without_product)
-def main [
-  1:array:num:3 <- create-array
-  index 1:array:num:3, 0
-]
-# just don't die
+void test_index_without_product() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  index 1:array:num:3, 0\n"
+      "]\n"
+  );
+  // just don't die
+}
 
 //:: To write to elements of arrays, use 'put'.
 
-:(scenario put_index)
-def main [
-  1:array:num:3 <- create-array
-  1:array:num <- put-index 1:array:num, 1, 34
-]
-+mem: storing 34 in location 3
+void test_put_index() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  1:array:num <- put-index 1:array:num, 1, 34\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 34 in location 3\n"
+  );
+}
 
 :(before "End Primitive Recipe Declarations")
 PUT_INDEX,
@@ -464,40 +540,61 @@ case PUT_INDEX: {
   break;
 }
 
-:(scenario put_index_out_of_bounds)
-% Hide_errors = true;
-def main [
-  1:array:point:3 <- create-array
-  8:point <- merge 34, 35
-  1:array:point <- put-index 1:array:point, 4, 8:point  # '4' is less than size of array in locations, but larger than its length in elements
-]
-+error: main: invalid index 4 in '1:array:point <- put-index 1:array:point, 4, 8:point'
-
-:(scenario put_index_out_of_bounds_2)
-% Hide_errors = true;
-def main [
-  1:array:point:3 <- create-array
-  10:point <- merge 34, 35
-  1:array:point <- put-index 1:array:point, -1, 10:point
-]
-+error: main: invalid index -1 in '1:array:point <- put-index 1:array:point, -1, 10:point'
-
-:(scenario put_index_product_error)
-% Hide_errors = true;
-def main [
-  1:array:num:3 <- create-array
-  4:array:num:3 <- put-index 1:array:num:3, 0, 34
-]
-+error: main: product of 'put-index' must be first ingredient '1:array:num:3', but got '4:array:num:3'
+:(code)
+void test_put_index_out_of_bounds() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:array:point:3 <- create-array\n"
+      "  8:point <- merge 34, 35\n"
+      "  1:array:point <- put-index 1:array:point, 4, 8:point\n"  // '4' is less than size of array in locations, but larger than its length in elements
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: invalid index 4 in '1:array:point <- put-index 1:array:point, 4, 8:point'\n"
+  );
+}
+
+void test_put_index_out_of_bounds_2() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:array:point:3 <- create-array\n"
+      "  10:point <- merge 34, 35\n"
+      "  1:array:point <- put-index 1:array:point, -1, 10:point\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: invalid index -1 in '1:array:point <- put-index 1:array:point, -1, 10:point'\n"
+  );
+}
+
+void test_put_index_product_error() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  4:array:num:3 <- put-index 1:array:num:3, 0, 34\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: product of 'put-index' must be first ingredient '1:array:num:3', but got '4:array:num:3'\n"
+  );
+}
 
 //:: compute the length of an array
 
-:(scenario array_length)
-def main [
-  1:array:num:3 <- create-array
-  10:num <- length 1:array:num
-]
-+mem: storing 3 in location 10
+void test_array_length() {
+  run(
+      "def main [\n"
+      "  1:array:num:3 <- create-array\n"
+      "  10:num <- length 1:array:num\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 3 in location 10\n"
+  );
+}
 
 :(before "End Primitive Recipe Declarations")
 LENGTH,