about summary refs log tree commit diff stats
path: root/034address.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 /034address.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 '034address.cc')
-rw-r--r--034address.cc342
1 files changed, 208 insertions, 134 deletions
diff --git a/034address.cc b/034address.cc
index af98fc14..bafde7b4 100644
--- a/034address.cc
+++ b/034address.cc
@@ -52,37 +52,54 @@ Next_alloc_id = 0;
 
 //: todo: give 'new' a custodian ingredient. Following malloc/free is a temporary hack.
 
-:(scenario new)
-# call 'new' two times with identical types without modifying the results; you
-# should get back different results
-def main [
-  10:&:num <- new num:type
-  12:&:num <- new num:type
-  20:bool <- equal 10:&:num, 12:&:num
-]
-+mem: storing 1000 in location 11
-+mem: storing 0 in location 20
-
-:(scenario new_array)
-# call 'new' with a second ingredient to allocate an array of some type rather than a single copy
-def main [
-  10:&:@:num <- new num:type, 5
-  12:&:num <- new num:type
-  20:num/alloc2, 21:num/alloc1 <- deaddress 10:&:@:num, 12:&:num
-  30:num <- subtract 21:num/alloc2, 20:num/alloc1
-]
-+run: {10: ("address" "array" "number")} <- new {num: "type"}, {5: "literal"}
-+mem: array length is 5
-# skip alloc id in allocation
-+mem: storing 1000 in location 11
-# don't forget the extra locations for alloc id and array length
-+mem: storing 7 in location 30
-
-:(scenario dilated_reagent_with_new)
-def main [
-  10:&:&:num <- new {(& num): type}
-]
-+new: size of '(& num)' is 2
+:(code)
+void test_new() {
+  run(
+      // call 'new' two times with identical types without modifying the
+      // results; you should get back different results
+      "def main [\n"
+      "  10:&:num <- new num:type\n"
+      "  12:&:num <- new num:type\n"
+      "  20:bool <- equal 10:&:num, 12:&:num\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 1000 in location 11\n"
+      "mem: storing 0 in location 20\n"
+  );
+}
+
+void test_new_array() {
+  run(
+      // call 'new' with a second ingredient to allocate an array of some type
+      // rather than a single copy
+      "def main [\n"
+      "  10:&:@:num <- new num:type, 5\n"
+      "  12:&:num <- new num:type\n"
+      "  20:num/alloc2, 21:num/alloc1 <- deaddress 10:&:@:num, 12:&:num\n"
+      "  30:num <- subtract 21:num/alloc2, 20:num/alloc1\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: {10: (\"address\" \"array\" \"number\")} <- new {num: \"type\"}, {5: \"literal\"}\n"
+      "mem: array length is 5\n"
+      // skip alloc id in allocation
+      "mem: storing 1000 in location 11\n"
+      // don't forget the extra locations for alloc id and array length
+      "mem: storing 7 in location 30\n"
+  );
+}
+
+void test_dilated_reagent_with_new() {
+  run(
+      "def main [\n"
+      "  10:&:&:num <- new {(& num): type}\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "new: size of '(& num)' is 2\n"
+  );
+}
 
 //: 'new' takes a weird 'type' as its first ingredient; don't error on it
 :(before "End Mu Types Initialization")
@@ -123,6 +140,7 @@ case NEW: {
   }
   break;
 }
+
 :(code)
 bool product_of_new_is_valid(const instruction& inst) {
   reagent/*copy*/ product = inst.products.at(0);
@@ -160,38 +178,59 @@ void drop_from_type(reagent& r, string expected_type) {
   delete tmp;
 }
 
-:(scenario new_returns_incorrect_type)
-% Hide_errors = true;
-def main [
-  1:bool <- new num:type
-]
-+error: main: product of 'new' has incorrect type: '1:bool <- new num:type'
-
-:(scenario new_discerns_singleton_list_from_atom_container)
-% Hide_errors = true;
-def main [
-  1:&:num <- new {(num): type}  # should be '{num: type}'
-]
-+error: main: product of 'new' has incorrect type: '1:&:num <- new {(num): type}'
-
-:(scenario new_with_type_abbreviation)
-def main [
-  1:&:num <- new num:type
-]
-$error: 0
-
-:(scenario new_with_type_abbreviation_inside_compound)
-def main [
-  {1: (address address number), raw: ()} <- new {(& num): type}
-]
-$error: 0
-
-:(scenario equal_result_of_new_with_null)
-def main [
-  1:&:num <- new num:type
-  10:bool <- equal 1:&:num, null
-]
-+mem: storing 0 in location 10
+void test_new_returns_incorrect_type() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:bool <- new num:type\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: product of 'new' has incorrect type: '1:bool <- new num:type'\n"
+  );
+}
+
+void test_new_discerns_singleton_list_from_atom_container() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:&:num <- new {(num): type}\n"  // should be '{num: type}'
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: product of 'new' has incorrect type: '1:&:num <- new {(num): type}'\n"
+  );
+}
+
+void test_new_with_type_abbreviation() {
+  run(
+      "def main [\n"
+      "  1:&:num <- new num:type\n"
+      "]\n"
+  );
+  CHECK_TRACE_COUNT("error", 0);
+}
+
+void test_new_with_type_abbreviation_inside_compound() {
+  run(
+      "def main [\n"
+      "  {1: (address address number), raw: ()} <- new {(& num): type}\n"
+      "]\n"
+  );
+  CHECK_TRACE_COUNT("error", 0);
+}
+
+void test_equal_result_of_new_with_null() {
+  run(
+      "def main [\n"
+      "  1:&:num <- new num:type\n"
+      "  10:bool <- equal 1:&:num, null\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 0 in location 10\n"
+  );
+}
 
 //: To implement 'new', a Mu transform turns all 'new' instructions into
 //: 'allocate' instructions that precompute the amount of memory they want to
@@ -329,79 +368,114 @@ void ensure_space(int size) {
   }
 }
 
-:(scenario new_initializes)
-% Memory_allocated_until = 10;
-% put(Memory, Memory_allocated_until, 1);
-def main [
-  1:&:num <- new num:type
-]
-+mem: storing 0 in location 10
-+mem: storing 0 in location 11
-+mem: storing 10 in location 2
-
-:(scenario new_initializes_alloc_id)
-% Memory_allocated_until = 10;
-% put(Memory, Memory_allocated_until, 1);
-% Next_alloc_id = 23;
-def main [
-  1:&:num <- new num:type
-]
-# initialize memory
-+mem: storing 0 in location 10
-+mem: storing 0 in location 11
-# alloc-id in payload
-+mem: storing alloc-id 23 in location 10
-# alloc-id in address
-+mem: storing 23 in location 1
-
-:(scenario new_size)
-def main [
-  10:&:num <- new num:type
-  12:&:num <- new num:type
-  20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num
-  30:num <- subtract 21:num/alloc2, 20:num/alloc1
-]
-# size of number + alloc id
-+mem: storing 2 in location 30
-
-:(scenario new_array_size)
-def main [
-  10:&:@:num <- new num:type, 5
-  12:&:num <- new num:type
-  20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num
-  30:num <- subtract 21:num/alloc2, 20:num/alloc1
-]
-# 5 locations for array contents + array length + alloc id
-+mem: storing 7 in location 30
-
-:(scenario new_empty_array)
-def main [
-  10:&:@:num <- new num:type, 0
-  12:&:num <- new num:type
-  20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:@:num, 12:&:num
-  30:num <- subtract 21:num/alloc2, 20:num/alloc1
-]
-+run: {10: ("address" "array" "number")} <- new {num: "type"}, {0: "literal"}
-+mem: array length is 0
-# one location for array length
-+mem: storing 2 in location 30
+void test_new_initializes() {
+  Memory_allocated_until = 10;
+  put(Memory, Memory_allocated_until, 1);
+  run(
+      "def main [\n"
+      "  1:&:num <- new num:type\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "mem: storing 0 in location 10\n"
+      "mem: storing 0 in location 11\n"
+      "mem: storing 10 in location 2\n"
+  );
+}
+
+void test_new_initializes_alloc_id() {
+  Memory_allocated_until = 10;
+  put(Memory, Memory_allocated_until, 1);
+  Next_alloc_id = 23;
+  run(
+      "def main [\n"
+      "  1:&:num <- new num:type\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      // initialize memory
+      "mem: storing 0 in location 10\n"
+      "mem: storing 0 in location 11\n"
+      // alloc-id in payload
+      "mem: storing alloc-id 23 in location 10\n"
+      // alloc-id in address
+      "mem: storing 23 in location 1\n"
+  );
+}
+
+void test_new_size() {
+  run(
+      "def main [\n"
+      "  10:&:num <- new num:type\n"
+      "  12:&:num <- new num:type\n"
+      "  20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num\n"
+      "  30:num <- subtract 21:num/alloc2, 20:num/alloc1\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      // size of number + alloc id
+      "mem: storing 2 in location 30\n"
+  );
+}
+
+void test_new_array_size() {
+  run(
+      "def main [\n"
+      "  10:&:@:num <- new num:type, 5\n"
+      "  12:&:num <- new num:type\n"
+      "  20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:num, 12:&:num\n"
+      "  30:num <- subtract 21:num/alloc2, 20:num/alloc1\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      // 5 locations for array contents + array length + alloc id
+      "mem: storing 7 in location 30\n"
+  );
+}
+
+void test_new_empty_array() {
+  run(
+      "def main [\n"
+      "  10:&:@:num <- new num:type, 0\n"
+      "  12:&:num <- new num:type\n"
+      "  20:num/alloc1, 21:num/alloc2 <- deaddress 10:&:@:num, 12:&:num\n"
+      "  30:num <- subtract 21:num/alloc2, 20:num/alloc1\n"
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: {10: (\"address\" \"array\" \"number\")} <- new {num: \"type\"}, {0: \"literal\"}\n"
+      "mem: array length is 0\n"
+      // one location for array length and one for alloc id
+      "mem: storing 2 in location 30\n"
+  );
+}
 
 //: If a routine runs out of its initial allocation, it should allocate more.
-:(scenario new_overflow)
-% Initial_memory_per_routine = 3;  // barely enough room for point allocation below
-def main [
-  10:&:num <- new num:type
-  12:&:point <- new point:type  # not enough room in initial page
-]
-+new: routine allocated memory from 1000 to 1003
-+new: routine allocated memory from 1003 to 1006
-
-:(scenario new_without_ingredient)
-% Hide_errors = true;
-def main [
-  1:&:num <- new  # missing ingredient
-]
-+error: main: 'new' requires one or two ingredients, but got '1:&:num <- new'
+void test_new_overflow() {
+  Initial_memory_per_routine = 3;  // barely enough room for point allocation below
+  run(
+      "def main [\n"
+      "  10:&:num <- new num:type\n"
+      "  12:&:point <- new point:type\n"  // not enough room in initial page
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "new: routine allocated memory from 1000 to 1003\n"
+      "new: routine allocated memory from 1003 to 1006\n"
+  );
+}
+
+void test_new_without_ingredient() {
+  Hide_errors = true;
+  run(
+      "def main [\n"
+      "  1:&:num <- new\n"  // missing ingredient
+      "]\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: main: 'new' requires one or two ingredients, but got '1:&:num <- new'\n"
+  );
+}
 
 //: a little helper: convert address to number