about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-09-30 00:07:33 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-09-30 01:01:34 -0700
commita0fc38c9e1584960ecad0eb8d58a5d4e6995ad87 (patch)
tree4a2c9a8120ea6d59dd371b575b0d1215a2c6aaaf
parentc6b3b04ac85f05065fef9626270d914dc4428484 (diff)
downloadmu-a0fc38c9e1584960ecad0eb8d58a5d4e6995ad87.tar.gz
2218 - check types in instructions much earlier
Front-loads it a bit more than I'd like, but the payoff is that other
recipes will now be able to describe the type checks right next to their
operation.

I'm also introducing a new use of /raw with literals to indicate unsafe
typecasts.
-rw-r--r--020check_type_by_instruction.cc97
-rw-r--r--020run.cc (renamed from 021run.cc)9
-rw-r--r--021check_type_by_instruction.cc99
-rw-r--r--030container.cc2
-rw-r--r--031address.cc51
-rw-r--r--032array.cc4
-rw-r--r--042name.cc13
-rw-r--r--044space.cc18
-rw-r--r--045space_surround.cc4
-rw-r--r--047global.cc4
-rw-r--r--048check_type_by_name.cc2
-rw-r--r--071print.mu4
-rw-r--r--edit/010-warnings.mu2
13 files changed, 177 insertions, 132 deletions
diff --git a/020check_type_by_instruction.cc b/020check_type_by_instruction.cc
deleted file mode 100644
index d1eb9694..00000000
--- a/020check_type_by_instruction.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-:(after "int main")
-  Transform.push_back(check_types_by_instruction);
-
-:(code)
-void check_types_by_instruction(const recipe_ordinal r) {
-  map<string, vector<type_ordinal> > metadata;
-  for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) {
-    instruction& inst = Recipe[r].steps.at(i);
-    switch (inst.operation) {
-      // Primitive Recipe Type Checks
-      case COPY: {
-        if (SIZE(inst.products) != SIZE(inst.ingredients)) {
-          raise << "ingredients and products should match in '" << inst.to_string() << "'\n" << end();
-          break;
-        }
-        for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
-          if (!is_mu_array(inst.ingredients.at(i)) && is_mu_array(inst.products.at(i))) {
-            raise << Recipe[r].name << ": can't copy " << inst.ingredients.at(i).original_string << " to array " << inst.products.at(i).original_string << "\n" << end();
-            goto finish_checking_instruction;
-          }
-          if (is_mu_array(inst.ingredients.at(i)) && !is_mu_array(inst.products.at(i))) {
-            raise << Recipe[r].name << ": can't copy array " << inst.ingredients.at(i).original_string << " to " << inst.products.at(i).original_string << "\n" << end();
-            goto finish_checking_instruction;
-          }
-        }
-        break;
-      }
-      // End Primitive Recipe Type Checks
-      default: {
-        // Defined Recipe Type Checks
-        // End Defined Recipe Type Checks
-      }
-    }
-    finish_checking_instruction:;
-  }
-}
-
-:(scenario copy_checks_reagent_count)
-% Hide_warnings = true;
-recipe main [
-  1:number <- copy 34, 35
-]
-+warn: ingredients and products should match in '1:number <- copy 34, 35'
-
-:(scenario write_scalar_to_array_disallowed)
-% Hide_warnings = true;
-recipe main [
-  1:array:number <- copy 34
-]
-+warn: main: can't copy 34 to array 1:array:number
-
-:(scenario write_scalar_to_array_disallowed_2)
-% Hide_warnings = true;
-recipe main [
-  1:number, 2:array:number <- copy 34, 35
-]
-+warn: main: can't copy 35 to array 2:array:number
-
-:(code)
-bool is_mu_array(reagent r) {
-  if (is_literal(r)) return false;
-  while (has_property(r, "lookup")) {
-    if (r.types.empty()) {
-      raise << "can't lookup non-address: " << r.original_string << '\n' << end();
-      return false;
-    }
-    if (r.types.at(0) != Type_ordinal["address"]) {
-      raise << "can't lookup non-address: " << r.original_string << '\n' << end();
-      return false;
-    }
-    r.types.erase(r.types.begin());
-    drop_one_lookup(r);
-  }
-  return !r.types.empty() && r.types.at(0) == Type_ordinal["array"];
-}
-
-void drop_one_lookup(reagent& r) {
-  for (vector<pair<string, vector<string> > >::iterator p = r.properties.begin(); p != r.properties.end(); ++p) {
-    if (p->first == "lookup") {
-      r.properties.erase(p);
-      return;
-    }
-  }
-  assert(false);
-}
-
-bool is_literal(const reagent& r) {
-  return SIZE(r.types) == 1 && r.types.at(0) == 0;
-}
-
-// helper for tests
-void run(string form) {
-  vector<recipe_ordinal> tmp = load(form);
-  transform_all();
-}
diff --git a/021run.cc b/020run.cc
index 514e1204..910a1d2d 100644
--- a/021run.cc
+++ b/020run.cc
@@ -271,6 +271,7 @@ void write_memory(reagent x, vector<double> data) {
     return;
   }
   for (long long int offset = 0; offset < SIZE(data); ++offset) {
+    if (base+offset == 0) continue;
     trace(Primitive_recipe_depth, "mem") << "storing " << no_scientific(data.at(offset)) << " in location " << base+offset << end();
     Memory[base+offset] = data.at(offset);
   }
@@ -299,7 +300,10 @@ bool is_dummy(const reagent& x) {
   return x.name == "_";
 }
 
-:(replace{} "void run(string form)")
+bool is_literal(const reagent& r) {
+  return SIZE(r.types) == 1 && r.types.at(0) == 0;
+}
+
 // helper for tests
 void run(string form) {
   vector<recipe_ordinal> tmp = load(form);
@@ -325,8 +329,9 @@ recipe main [
 +run: _ <- copy 0
 
 :(scenario write_to_0_disallowed)
+% Hide_warnings = true;
 recipe main [
-  0 <- copy 34
+  0:number <- copy 34
 ]
 -mem: storing 34 in location 0
 
diff --git a/021check_type_by_instruction.cc b/021check_type_by_instruction.cc
new file mode 100644
index 00000000..33668808
--- /dev/null
+++ b/021check_type_by_instruction.cc
@@ -0,0 +1,99 @@
+//: Introduce a new transform to check types in instructions before we start
+//: running them. It'll be extensible, so that we can add type checks for new
+//: recipes as we extend 'run' to support them.
+
+:(after "int main")
+  Transform.push_back(check_types_by_instruction);
+
+:(code)
+void check_types_by_instruction(const recipe_ordinal r) {
+  if (Trace_stream && trace_count("warn") > 0) return;
+  map<string, vector<type_ordinal> > metadata;
+  for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) {
+    instruction& inst = Recipe[r].steps.at(i);
+    switch (inst.operation) {
+      // Primitive Recipe Type Checks
+      case COPY: {
+        if (SIZE(inst.products) != SIZE(inst.ingredients)) {
+          raise << "ingredients and products should match in '" << inst.to_string() << "'\n" << end();
+          break;
+        }
+        for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
+          if (!types_match(inst.products.at(i), inst.ingredients.at(i))) {
+            raise << Recipe[r].name << ": can't copy " << inst.ingredients.at(i).original_string << " to " << inst.products.at(i).original_string << "; types don't match\n" << end();
+            goto finish_checking_instruction;
+          }
+        }
+        break;
+      }
+      // End Primitive Recipe Type Checks
+      default: {
+        // Defined Recipe Type Checks
+        // End Defined Recipe Type Checks
+      }
+    }
+    finish_checking_instruction:;
+  }
+}
+
+:(scenario copy_checks_reagent_count)
+% Hide_warnings = true;
+recipe main [
+  1:number <- copy 34, 35
+]
++warn: ingredients and products should match in '1:number <- copy 34, 35'
+
+:(scenario write_scalar_to_array_disallowed)
+% Hide_warnings = true;
+recipe main [
+  1:array:number <- copy 34
+]
++warn: main: can't copy 34 to 1:array:number; types don't match
+
+:(scenario write_scalar_to_array_disallowed_2)
+% Hide_warnings = true;
+recipe main [
+  1:number, 2:array:number <- copy 34, 35
+]
++warn: main: can't copy 35 to 2:array:number; types don't match
+
+:(scenario write_scalar_to_address_disallowed)
+% Hide_warnings = true;
+recipe main [
+  1:address:number <- copy 34
+]
++warn: main: can't copy 34 to 1:address:number; types don't match
+
+:(code)
+bool types_match(reagent lhs, reagent rhs) {
+  // '_' never raises type error
+  if (is_dummy(lhs)) return true;
+  // to sidestep type-checking, use /raw in the source.
+  // this is unsafe, and will be highlighted in red inside vim. just for some tests.
+  if (is_raw(rhs)) return true;
+  // allow writing 0 to any address
+  if (rhs.name == "0" && is_mu_address(lhs)) return true;
+  if (is_literal(rhs)) return !is_mu_array(lhs) && !is_mu_address(lhs) && size_of(rhs) == size_of(lhs);
+  // more refined types can always be copied to less refined ones
+  if (SIZE(lhs.types) > SIZE(rhs.types)) return false;
+  if (SIZE(lhs.types) == SIZE(rhs.types)) return lhs.types == rhs.types;
+  rhs.types.resize(SIZE(lhs.types));
+  return lhs.types == rhs.types;
+}
+
+bool is_raw(const reagent& r) {
+  for (long long int i = /*skip value+type*/1; i < SIZE(r.properties); ++i) {
+    if (r.properties.at(i).first == "raw") return true;
+  }
+  return false;
+}
+
+bool is_mu_array(reagent r) {
+  if (is_literal(r)) return false;
+  return !r.types.empty() && r.types.at(0) == Type_ordinal["array"];
+}
+
+bool is_mu_address(reagent r) {
+  if (is_literal(r)) return false;
+  return !r.types.empty() && r.types.at(0) == Type_ordinal["address"];
+}
diff --git a/030container.cc b/030container.cc
index 8505f475..19d3a5af 100644
--- a/030container.cc
+++ b/030container.cc
@@ -402,7 +402,7 @@ recipe main [
 :(scenario run_allows_type_definition_after_use)
 % Hide_warnings = true;
 recipe main [
-  1:bar <- copy 0
+  1:bar <- copy 0/raw
 ]
 
 container bar [
diff --git a/031address.cc b/031address.cc
index 22a57189..7c4f1e34 100644
--- a/031address.cc
+++ b/031address.cc
@@ -3,7 +3,7 @@
 
 :(scenario copy_indirect)
 recipe main [
-  1:address:number <- copy 2
+  1:address:number <- copy 2/raw
   2:number <- copy 34
   # This loads location 1 as an address and looks up *that* location.
   3:number <- copy 1:address:number/lookup
@@ -17,7 +17,7 @@ x = canonize(x);
 //: 'lookup' property
 :(scenario store_indirect)
 recipe main [
-  1:address:number <- copy 2
+  1:address:number <- copy 2/raw
   1:address:number/lookup <- copy 34
 ]
 +mem: storing 34 in location 2
@@ -80,6 +80,51 @@ reagent lookup_memory(reagent x) {
   return result;
 }
 
+:(after "bool types_match(reagent lhs, reagent rhs)")
+if (!canonize_type(lhs)) return false;
+if (!canonize_type(rhs)) return false;
+
+:(replace{} "bool is_mu_array(reagent r)")
+bool is_mu_array(reagent r) {
+  if (is_literal(r)) return false;
+  if (!canonize_type(r)) return false;
+  return !r.types.empty() && r.types.at(0) == Type_ordinal["array"];
+}
+
+:(replace{} "bool is_mu_address(reagent r)")
+bool is_mu_address(reagent r) {
+  if (is_literal(r)) return false;
+  if (!canonize_type(r)) return false;
+  return !r.types.empty() && r.types.at(0) == Type_ordinal["address"];
+}
+
+:(code)
+bool canonize_type(reagent& r) {
+  while (has_property(r, "lookup")) {
+    if (r.types.empty()) {
+      raise << "can't lookup non-address: " << r.original_string << '\n' << end();
+      return false;
+    }
+    if (r.types.at(0) != Type_ordinal["address"]) {
+      raise << "can't lookup non-address: " << r.original_string << '\n' << end();
+      return false;
+    }
+    r.types.erase(r.types.begin());
+    drop_one_lookup(r);
+  }
+  return true;
+}
+
+void drop_one_lookup(reagent& r) {
+  for (vector<pair<string, vector<string> > >::iterator p = r.properties.begin(); p != r.properties.end(); ++p) {
+    if (p->first == "lookup") {
+      r.properties.erase(p);
+      return;
+    }
+  }
+  assert(false);
+}
+
 //:: 'get' can read from container address
 :(scenario get_indirect)
 recipe main [
@@ -119,7 +164,7 @@ base = canonize(base);
 
 :(scenario lookup_abbreviation)
 recipe main [
-  1:address:number <- copy 2
+  1:address:number <- copy 2/raw
   2:number <- copy 34
   3:number <- copy *1:address:number
 ]
diff --git a/032array.cc b/032array.cc
index 1b8d5900..31f4832e 100644
--- a/032array.cc
+++ b/032array.cc
@@ -78,7 +78,7 @@ recipe main [
   2:number <- copy 14
   3:number <- copy 15
   4:number <- copy 16
-  5:address:array:number <- copy 1  # unsafe
+  5:address:array:number <- copy 1/raw
   6:array:number <- copy *5:address:array:number
 ]
 +mem: storing 3 in location 6
@@ -180,7 +180,7 @@ recipe main [
   2:number <- copy 14
   3:number <- copy 15
   4:number <- copy 16
-  5:address:array:number <- copy 1
+  5:address:array:number <- copy 1/raw
   6:number <- index *5:address:array:number, 1
 ]
 +mem: storing 15 in location 6
diff --git a/042name.cc b/042name.cc
index c430a10f..060f59e1 100644
--- a/042name.cc
+++ b/042name.cc
@@ -66,7 +66,7 @@ void transform_names(const recipe_ordinal r) {
 
 bool disqualified(/*mutable*/ reagent& x, const instruction& inst) {
   if (x.types.empty()) {
-    raise << "missing type in '" << inst.to_string() << "'\n" << end();
+    raise << "missing type for " << x.original_string << " in '" << inst.to_string() << "'\n" << end();
     return true;
   }
   if (is_raw(x)) return true;
@@ -116,13 +116,6 @@ bool is_named_location(const reagent& x) {
   return !is_integer(x.name);
 }
 
-bool is_raw(const reagent& r) {
-  for (long long int i = /*skip value+type*/1; i < SIZE(r.properties); ++i) {
-    if (r.properties.at(i).first == "raw") return true;
-  }
-  return false;
-}
-
 bool is_special_name(const string& s) {
   if (s == "_") return true;
   if (s == "0") return true;
@@ -217,8 +210,8 @@ if (inst.operation == Recipe_ordinal["get"]
 :(scenarios transform)
 :(scenario transform_names_handles_containers)
 recipe main [
-  a:point <- copy 0
-  b:number <- copy 0
+  a:point <- copy 0/raw
+  b:number <- copy 0/raw
 ]
 +name: assign a 1
 +name: assign b 3
diff --git a/044space.cc b/044space.cc
index eddf457e..a9251364 100644
--- a/044space.cc
+++ b/044space.cc
@@ -7,7 +7,7 @@
 # then location 0 is really location 11, location 1 is really location 12, and so on.
 recipe main [
   10:number <- copy 5  # pretend array; in practice we'll use new
-  default-space:address:array:location <- copy 10
+  default-space:address:array:location <- copy 10/raw
   1:number <- copy 23
 ]
 +mem: storing 23 in location 12
@@ -19,8 +19,8 @@ recipe main [
   # pretend array
   1000:number <- copy 5
   # actual start of this recipe
-  default-space:address:array:location <- copy 1000
-  1:address:number <- copy 3
+  default-space:address:array:location <- copy 1000/raw
+  1:address:number <- copy 3/raw
   8:number/raw <- copy *1:address:number
 ]
 +mem: storing 34 in location 8
@@ -74,8 +74,8 @@ recipe main [
   # pretend array
   1000:number <- copy 5
   # actual start of this recipe
-  default-space:address:array:location <- copy 1000
-  1:address:point <- copy 12
+  default-space:address:array:location <- copy 1000/raw
+  1:address:point <- copy 12/raw
   9:number/raw <- get *1:address:point, 1:offset
 ]
 +mem: storing 35 in location 9
@@ -94,8 +94,8 @@ recipe main [
   # pretend array
   1000:number <- copy 5
   # actual start of this recipe
-  default-space:address:array:location <- copy 1000
-  1:address:array:number <- copy 12
+  default-space:address:array:location <- copy 1000/raw
+  1:address:array:number <- copy 12/raw
   9:number/raw <- index *1:address:array:number, 1
 ]
 +mem: storing 35 in location 9
@@ -219,8 +219,8 @@ long long int address(long long int offset, long long int base) {
 
 :(scenario get_default_space)
 recipe main [
-  default-space:address:array:location <- copy 10
-  1:number/raw <- copy default-space:address:array:location
+  default-space:address:array:location <- copy 10/raw
+  1:address:array:location/raw <- copy default-space:address:array:location
 ]
 +mem: storing 10 in location 1
 
diff --git a/045space_surround.cc b/045space_surround.cc
index fee7ef71..68d23c93 100644
--- a/045space_surround.cc
+++ b/045space_surround.cc
@@ -9,8 +9,8 @@
 recipe main [
   10:number <- copy 5  # pretend array
   20:number <- copy 5  # pretend array
-  default-space:address:array:location <- copy 10
-  0:address:array:location/names:dummy <- copy 20  # later layers will explain the /names: property
+  default-space:address:array:location <- copy 10/raw
+  0:address:array:location/names:dummy <- copy 20/raw  # later layers will explain the /names: property
   1:number <- copy 32
   1:number/space:1 <- copy 33
 ]
diff --git a/047global.cc b/047global.cc
index faa731c1..90646f49 100644
--- a/047global.cc
+++ b/047global.cc
@@ -9,8 +9,8 @@ recipe main [
   10:number <- copy 5
   20:number <- copy 5
   # actual start of this recipe
-  global-space:address:array:location <- copy 20
-  default-space:address:array:location <- copy 10
+  global-space:address:array:location <- copy 20/raw
+  default-space:address:array:location <- copy 10/raw
   1:number <- copy 23
   1:number/space:global <- copy 24
 ]
diff --git a/048check_type_by_name.cc b/048check_type_by_name.cc
index 8510d02c..e4b4f6d0 100644
--- a/048check_type_by_name.cc
+++ b/048check_type_by_name.cc
@@ -80,7 +80,7 @@ recipe main [
   x <- copy 1
   x:number <- copy 2
 ]
-+warn: missing type in 'x <- copy 1'
++warn: missing type for x in 'x <- copy 1'
 
 :(scenario typo_in_address_type_warns)
 % Hide_warnings = true;
diff --git a/071print.mu b/071print.mu
index 6aeb2042..2789a0ed 100644
--- a/071print.mu
+++ b/071print.mu
@@ -47,8 +47,8 @@ recipe clear-screen [
       break-if done?
       curr:address:screen-cell <- index-address *buf, i
       curr-content:address:character <- get-address *curr, contents:offset
-      *curr-content <- copy [ ]
-      curr-color:address:character <- get-address *curr, color:offset
+      *curr-content <- copy 0/empty
+      curr-color:address:number <- get-address *curr, color:offset
       *curr-color <- copy 7/white
       i <- add i, 1
       loop
diff --git a/edit/010-warnings.mu b/edit/010-warnings.mu
index f5244a8d..6c93a95c 100644
--- a/edit/010-warnings.mu
+++ b/edit/010-warnings.mu
@@ -132,7 +132,7 @@ recipe foo [
     .recipe foo [                                      ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .  x <- copy 0                                     ┊                                                 .
     .]                                                 ┊                                                 .
-    .missing type in 'x <- copy 0'                     ┊                                                 .
+    .missing type for x in 'x <- copy 0'               ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
     .                                                  ┊                                                 .
   ]