diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2015-09-30 00:07:33 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2015-09-30 01:01:34 -0700 |
commit | a0fc38c9e1584960ecad0eb8d58a5d4e6995ad87 (patch) | |
tree | 4a2c9a8120ea6d59dd371b575b0d1215a2c6aaaf | |
parent | c6b3b04ac85f05065fef9626270d914dc4428484 (diff) | |
download | mu-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.cc | 97 | ||||
-rw-r--r-- | 020run.cc (renamed from 021run.cc) | 9 | ||||
-rw-r--r-- | 021check_type_by_instruction.cc | 99 | ||||
-rw-r--r-- | 030container.cc | 2 | ||||
-rw-r--r-- | 031address.cc | 51 | ||||
-rw-r--r-- | 032array.cc | 4 | ||||
-rw-r--r-- | 042name.cc | 13 | ||||
-rw-r--r-- | 044space.cc | 18 | ||||
-rw-r--r-- | 045space_surround.cc | 4 | ||||
-rw-r--r-- | 047global.cc | 4 | ||||
-rw-r--r-- | 048check_type_by_name.cc | 2 | ||||
-rw-r--r-- | 071print.mu | 4 | ||||
-rw-r--r-- | edit/010-warnings.mu | 2 |
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' ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] |