2015-10-01 06:31:43 +00:00
|
|
|
//: Introduce a new transform to perform various checks in instructions before
|
|
|
|
//: we start running them. It'll be extensible, so that we can add checks for
|
|
|
|
//: new recipes as we extend 'run' to support them.
|
2015-10-01 23:25:25 +00:00
|
|
|
//:
|
|
|
|
//: Doing checking in a separate part complicates things, because the values
|
|
|
|
//: of variables in memory and the processor (current_recipe_name,
|
|
|
|
//: current_instruction) aren't available at checking time. If I had a more
|
|
|
|
//: sophisticated layer system I'd introduce the simpler version first and
|
|
|
|
//: transform it in a separate layer or set of layers.
|
2015-09-30 07:07:33 +00:00
|
|
|
|
|
|
|
:(after "int main")
|
2015-09-30 09:01:59 +00:00
|
|
|
Transform.push_back(check_instruction);
|
2015-09-30 07:07:33 +00:00
|
|
|
|
|
|
|
:(code)
|
2015-09-30 09:01:59 +00:00
|
|
|
void check_instruction(const recipe_ordinal r) {
|
2015-09-30 07:07:33 +00:00
|
|
|
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) {
|
2015-09-30 09:01:59 +00:00
|
|
|
// Primitive Recipe Checks
|
2015-09-30 07:07:33 +00:00
|
|
|
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))) {
|
2015-10-01 20:13:10 +00:00
|
|
|
raise << maybe(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();
|
2015-09-30 07:07:33 +00:00
|
|
|
goto finish_checking_instruction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-09-30 09:01:59 +00:00
|
|
|
// End Primitive Recipe Checks
|
2015-09-30 07:07:33 +00:00
|
|
|
default: {
|
2015-09-30 09:01:59 +00:00
|
|
|
// Defined Recipe Checks
|
|
|
|
// End Defined Recipe Checks
|
2015-09-30 07:07:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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"];
|
|
|
|
}
|
2015-09-30 08:40:15 +00:00
|
|
|
|
|
|
|
bool is_mu_number(reagent r) {
|
|
|
|
if (is_literal(r))
|
|
|
|
return r.properties.at(0).second.at(0) == "literal-number"
|
|
|
|
|| r.properties.at(0).second.at(0) == "literal";
|
|
|
|
if (r.types.empty()) return false;
|
|
|
|
if (r.types.at(0) == Type_ordinal["character"]) return true; // permit arithmetic on unicode code points
|
2015-09-30 08:57:23 +00:00
|
|
|
return r.types.at(0) == Type_ordinal["number"];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_mu_scalar(reagent r) {
|
|
|
|
if (is_literal(r))
|
2015-09-30 09:18:05 +00:00
|
|
|
return r.properties.at(0).second.empty() || r.properties.at(0).second.at(0) != "literal-string";
|
2015-09-30 08:57:23 +00:00
|
|
|
if (is_mu_array(r)) return false;
|
|
|
|
return size_of(r) == 1;
|
2015-09-30 08:40:15 +00:00
|
|
|
}
|