diff options
-rw-r--r-- | 021check_type_by_instruction.cc | 9 | ||||
-rw-r--r-- | 022arithmetic.cc | 143 | ||||
-rw-r--r-- | 031address.cc | 3 |
3 files changed, 121 insertions, 34 deletions
diff --git a/021check_type_by_instruction.cc b/021check_type_by_instruction.cc index 33668808..0ac5a530 100644 --- a/021check_type_by_instruction.cc +++ b/021check_type_by_instruction.cc @@ -97,3 +97,12 @@ bool is_mu_address(reagent r) { if (is_literal(r)) return false; return !r.types.empty() && r.types.at(0) == Type_ordinal["address"]; } + +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 + return !r.types.empty() && r.types.at(0) == Type_ordinal["number"]; +} diff --git a/022arithmetic.cc b/022arithmetic.cc index 107f0853..9eca7e11 100644 --- a/022arithmetic.cc +++ b/022arithmetic.cc @@ -4,14 +4,28 @@ ADD, :(before "End Primitive Recipe Numbers") Recipe_ordinal["add"] = ADD; +:(before "End Primitive Recipe Type Checks") +case ADD: { + for (long long int i = 0; i < SIZE(inst.ingredients); ++i) { + if (!is_mu_number(inst.ingredients.at(i))) { + raise << Recipe[r].name << ": 'add' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end(); + goto finish_checking_instruction; + } + } + if (SIZE(inst.products) > 1) { + raise << Recipe[r].name << ": 'add' yields exactly one product in '" << inst.to_string() << "'\n" << end(); + break; + } + if (!inst.products.empty() && !is_mu_number(inst.products.at(0))) { + raise << Recipe[r].name << ": 'add' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end(); + break; + } + break; +} :(before "End Primitive Recipe Implementations") case ADD: { double result = 0; for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'add' requires number ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } result += ingredients.at(i).at(0); } products.resize(1); @@ -39,28 +53,52 @@ recipe main [ ] +mem: storing 12 in location 1 +:(scenario add_checks_type) +% Hide_warnings = true; +recipe main [ + 1:number <- add 2:boolean, 1 +] ++warn: main: 'add' requires number ingredients, but got 2:boolean + +:(scenario add_checks_return_type) +% Hide_warnings = true; +recipe main [ + 1:address:number <- add 2, 2 +] ++warn: main: 'add' should yield a number, but got 1:address:number + :(before "End Primitive Recipe Declarations") SUBTRACT, :(before "End Primitive Recipe Numbers") Recipe_ordinal["subtract"] = SUBTRACT; -:(before "End Primitive Recipe Implementations") +:(before "End Primitive Recipe Type Checks") case SUBTRACT: { - if (ingredients.empty()) { - raise << current_recipe_name() << ": 'subtract' has no ingredients\n" << end(); + if (inst.ingredients.empty()) { + raise << Recipe[r].name << ": 'subtract' has no ingredients\n" << end(); + break; + } + for (long long int i = 0; i < SIZE(inst.ingredients); ++i) { + if (is_raw(inst.ingredients.at(i))) continue; // offsets in tests + if (!is_mu_number(inst.ingredients.at(i))) { + raise << Recipe[r].name << ": 'subtract' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end(); + goto finish_checking_instruction; + } + } + if (SIZE(inst.products) > 1) { + raise << Recipe[r].name << ": 'subtract' yields exactly one product in '" << inst.to_string() << "'\n" << end(); break; } - if (!scalar(ingredients.at(0))) { - raise << current_recipe_name() << ": 'subtract' requires number ingredients, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + if (!inst.products.empty() && !is_mu_number(inst.products.at(0))) { + raise << Recipe[r].name << ": 'subtract' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end(); break; } + break; +} +:(before "End Primitive Recipe Implementations") +case SUBTRACT: { double result = ingredients.at(0).at(0); - for (long long int i = 1; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'subtract' requires number ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } + for (long long int i = 1; i < SIZE(ingredients); ++i) result -= ingredients.at(i).at(0); - } products.resize(1); products.at(0).push_back(result); break; @@ -90,14 +128,28 @@ recipe main [ MULTIPLY, :(before "End Primitive Recipe Numbers") Recipe_ordinal["multiply"] = MULTIPLY; +:(before "End Primitive Recipe Type Checks") +case MULTIPLY: { + for (long long int i = 0; i < SIZE(inst.ingredients); ++i) { + if (!is_mu_number(inst.ingredients.at(i))) { + raise << Recipe[r].name << ": 'add' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end(); + goto finish_checking_instruction; + } + } + if (SIZE(inst.products) > 1) { + raise << Recipe[r].name << ": 'multiply' yields exactly one product in '" << inst.to_string() << "'\n" << end(); + break; + } + if (!inst.products.empty() && !is_mu_number(inst.products.at(0))) { + raise << Recipe[r].name << ": 'multiply' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end(); + break; + } + break; +} :(before "End Primitive Recipe Implementations") case MULTIPLY: { double result = 1; for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'multiply' requires number ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } result *= ingredients.at(i).at(0); } products.resize(1); @@ -129,24 +181,33 @@ recipe main [ DIVIDE, :(before "End Primitive Recipe Numbers") Recipe_ordinal["divide"] = DIVIDE; -:(before "End Primitive Recipe Implementations") +:(before "End Primitive Recipe Type Checks") case DIVIDE: { - if (ingredients.empty()) { - raise << current_recipe_name() << ": 'divide' has no ingredients\n" << end(); + if (inst.ingredients.empty()) { + raise << Recipe[r].name << ": 'divide' has no ingredients\n" << end(); break; } - if (!scalar(ingredients.at(0))) { - raise << current_recipe_name() << ": 'divide' requires number ingredients, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + for (long long int i = 0; i < SIZE(inst.ingredients); ++i) { + if (!is_mu_number(inst.ingredients.at(i))) { + raise << Recipe[r].name << ": 'divide' requires number ingredients, but got " << inst.ingredients.at(i).original_string << '\n' << end(); + goto finish_checking_instruction; + } + } + if (SIZE(inst.products) > 1) { + raise << Recipe[r].name << ": 'divide' yields exactly one product in '" << inst.to_string() << "'\n" << end(); break; } + if (!inst.products.empty() && !is_mu_number(inst.products.at(0))) { + raise << Recipe[r].name << ": 'divide' should yield a number, but got " << inst.products.at(0).original_string << '\n' << end(); + break; + } + break; +} +:(before "End Primitive Recipe Implementations") +case DIVIDE: { double result = ingredients.at(0).at(0); - for (long long int i = 1; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'divide' requires number ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } + for (long long int i = 1; i < SIZE(ingredients); ++i) result /= ingredients.at(i).at(0); - } products.resize(1); products.at(0).push_back(result); break; @@ -178,17 +239,31 @@ recipe main [ DIVIDE_WITH_REMAINDER, :(before "End Primitive Recipe Numbers") Recipe_ordinal["divide-with-remainder"] = DIVIDE_WITH_REMAINDER; -:(before "End Primitive Recipe Implementations") +:(before "End Primitive Recipe Type Checks") case DIVIDE_WITH_REMAINDER: { - products.resize(2); - if (SIZE(ingredients) != 2) { + if (SIZE(inst.ingredients) != 2) { raise << current_recipe_name() << ": 'divide-with-remainder' requires exactly two ingredients, but got '" << current_instruction().to_string() << "'\n" << end(); break; } - if (!scalar(ingredients.at(0)) || !scalar(ingredients.at(1))) { + if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) { raise << current_recipe_name() << ": 'divide-with-remainder' requires number ingredients, but got '" << current_instruction().to_string() << "'\n" << end(); break; } + if (SIZE(inst.products) > 2) { + raise << Recipe[r].name << ": 'divide-with-remainder' yields two products in '" << inst.to_string() << "'\n" << end(); + break; + } + for (long long int i = 0; i < SIZE(inst.products); ++i) { + if (!is_mu_number(inst.products.at(i))) { + raise << Recipe[r].name << ": 'divide-with-remainder' should yield a number, but got " << inst.products.at(i).original_string << '\n' << end(); + goto finish_checking_instruction; + } + } + break; +} +:(before "End Primitive Recipe Implementations") +case DIVIDE_WITH_REMAINDER: { + products.resize(2); long long int a = static_cast<long long int>(ingredients.at(0).at(0)); long long int b = static_cast<long long int>(ingredients.at(1).at(0)); if (b == 0) { diff --git a/031address.cc b/031address.cc index 46c38fd6..5514b8c2 100644 --- a/031address.cc +++ b/031address.cc @@ -90,6 +90,9 @@ reagent lookup_memory(reagent x) { :(after "bool is_mu_address(reagent r)") if (!canonize_type(r)) return false; +:(after "bool is_mu_number(reagent r)") + if (!canonize_type(r)) return false; + :(code) bool canonize_type(reagent& r) { while (has_property(r, "lookup")) { |