From bb58e1797e8a813891d2ba8ceabcfb518c6f8050 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 28 Sep 2015 23:36:39 -0700 Subject: 2214 --- 020_check_type_by_instruction.cc | 97 +++++++++++ 020run.cc | 362 --------------------------------------- 021arithmetic.cc | 248 --------------------------- 021run.cc | 358 ++++++++++++++++++++++++++++++++++++++ 022arithmetic.cc | 248 +++++++++++++++++++++++++++ 022boolean.cc | 123 ------------- 023boolean.cc | 123 +++++++++++++ 023jump.cc | 141 --------------- 024compare.cc | 296 -------------------------------- 024jump.cc | 141 +++++++++++++++ 025compare.cc | 296 ++++++++++++++++++++++++++++++++ 032array.cc | 28 --- 048_check_type_by_instruction.cc | 59 ------- 048_check_type_by_name.cc | 91 ++++++++++ 049_check_type_by_name.cc | 91 ---------- 15 files changed, 1354 insertions(+), 1348 deletions(-) create mode 100644 020_check_type_by_instruction.cc delete mode 100644 020run.cc delete mode 100644 021arithmetic.cc create mode 100644 021run.cc create mode 100644 022arithmetic.cc delete mode 100644 022boolean.cc create mode 100644 023boolean.cc delete mode 100644 023jump.cc delete mode 100644 024compare.cc create mode 100644 024jump.cc create mode 100644 025compare.cc delete mode 100644 048_check_type_by_instruction.cc create mode 100644 048_check_type_by_name.cc delete mode 100644 049_check_type_by_name.cc diff --git a/020_check_type_by_instruction.cc b/020_check_type_by_instruction.cc new file mode 100644 index 00000000..d1eb9694 --- /dev/null +++ b/020_check_type_by_instruction.cc @@ -0,0 +1,97 @@ + + +:(after "int main") + Transform.push_back(check_types_by_instruction); + +:(code) +void check_types_by_instruction(const recipe_ordinal r) { + map > 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 > >::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 tmp = load(form); + transform_all(); +} diff --git a/020run.cc b/020run.cc deleted file mode 100644 index 116d44a7..00000000 --- a/020run.cc +++ /dev/null @@ -1,362 +0,0 @@ -//: Phase 3: Start running a loaded and transformed recipe. -//: -//: So far we've seen recipes as lists of instructions, and instructions point -//: at other recipes. To kick things off mu needs to know how to run certain -//: 'primitive' recipes. That will then give the ability to run recipes -//: containing these primitives. -//: -//: This layer defines a skeleton with just two primitive recipes: IDLE which -//: does nothing, and COPY, which can copy numbers from one memory location to -//: another. Later layers will add more primitives. - -:(scenario copy_literal) -recipe main [ - 1:number <- copy 23 -] -+run: 1:number <- copy 23 -+mem: storing 23 in location 1 - -:(scenario copy) -recipe main [ - 1:number <- copy 23 - 2:number <- copy 1:number -] -+run: 2:number <- copy 1:number -+mem: location 1 is 23 -+mem: storing 23 in location 2 - -:(scenario copy_multiple) -recipe main [ - 1:number, 2:number <- copy 23, 24 -] -+mem: storing 23 in location 1 -+mem: storing 24 in location 2 - -:(before "End Types") -// Book-keeping while running a recipe. -//: Later layers will change this. -struct routine { - recipe_ordinal running_recipe; - long long int running_step_index; - routine(recipe_ordinal r) :running_recipe(r), running_step_index(0) {} - bool completed() const; -}; - -:(before "End Globals") -routine* Current_routine = NULL; -map Instructions_running; -map Locations_read; -map Locations_read_by_instruction; - -:(code) -void run(recipe_ordinal r) { - routine rr(r); - Current_routine = &rr; - run_current_routine(); -} - -void run_current_routine() -{ // curly on a separate line, because later layers will modify header - while (!Current_routine->completed()) // later layers will modify condition - { - // Running One Instruction -//? Instructions_running[current_recipe_name()]++; - if (current_instruction().is_label) { ++current_step_index(); continue; } - trace(Initial_callstack_depth+Callstack_depth, "run") << current_instruction().to_string() << end(); - if (Memory[0] != 0) { - raise << "something wrote to location 0; this should never happen\n" << end(); - Memory[0] = 0; - } - // Read all ingredients from memory. - // Each ingredient loads a vector of values rather than a single value; mu - // permits operating on reagents spanning multiple locations. - vector > ingredients; - if (should_copy_ingredients()) { - for (long long int i = 0; i < SIZE(current_instruction().ingredients); ++i) { - ingredients.push_back(read_memory(current_instruction().ingredients.at(i))); -//? Locations_read[current_recipe_name()] += SIZE(ingredients.back()); -//? Locations_read_by_instruction[current_instruction().name] += SIZE(ingredients.back()); - } - } - // Instructions below will write to 'products'. - vector > products; - switch (current_instruction().operation) { - // Primitive Recipe Implementations - case COPY: { - copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin())); - break; - } - // End Primitive Recipe Implementations - default: { - cout << "not a primitive op: " << current_instruction().operation << '\n'; - } - } - finish_instruction: - if (SIZE(products) < SIZE(current_instruction().products)) { - raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string() << end(); - } - else { - for (long long int i = 0; i < SIZE(current_instruction().products); ++i) { - write_memory(current_instruction().products.at(i), products.at(i)); - } - } - // End of Instruction - ++current_step_index(); - } - stop_running_current_routine:; -} - -bool should_copy_ingredients() { - // End should_copy_ingredients Special-cases - return true; -} - -//: Some helpers. -//: We'll need to override these later as we change the definition of routine. -//: Important that they return referrences into the routine. - -inline long long int& current_step_index() { - return Current_routine->running_step_index; -} - -inline const string& current_recipe_name() { - return Recipe[Current_routine->running_recipe].name; -} - -inline const instruction& current_instruction() { - return Recipe[Current_routine->running_recipe].steps.at(Current_routine->running_step_index); -} - -inline bool routine::completed() const { - return running_step_index >= SIZE(Recipe[running_recipe].steps); -} - -//:: Startup flow - -//: Step 1: load all .mu files with numeric prefixes (in order) -:(before "End Load Recipes") -load_permanently("core.mu"); -transform_all(); - -//: Step 2: load any .mu files provided at the commandline -:(before "End Commandline Parsing") -if (argc > 1) { - // skip argv[0] - argv++; - argc--; - // ignore argv past '--'; that's commandline args for 'main' - while (argc > 0) { - if (string(*argv) == "--") break; - load_permanently(*argv); - argv++; - argc--; - } - transform_all(); - if (Run_tests) Recipe.erase(Recipe_ordinal[string("main")]); -} - -//: Step 3: if we aren't running tests, locate a recipe called 'main' and -//: start running it. -:(before "End Main") -if (!Run_tests) { - setup(); -//? Trace_file = "interactive"; -//? START_TRACING_UNTIL_END_OF_SCOPE; -//? Trace_stream->collect_layers.insert("app"); - run_main(argc, argv); - teardown(); -} - -:(code) -void run_main(int argc, char* argv[]) { - recipe_ordinal r = Recipe_ordinal[string("main")]; - if (r) run(r); -} - -:(code) -void dump_profile() { - for (map::iterator p = Instructions_running.begin(); p != Instructions_running.end(); ++p) { - cerr << p->first << ": " << p->second << '\n'; - } - cerr << "== locations read\n"; - for (map::iterator p = Locations_read.begin(); p != Locations_read.end(); ++p) { - cerr << p->first << ": " << p->second << '\n'; - } - cerr << "== locations read by instruction\n"; - for (map::iterator p = Locations_read_by_instruction.begin(); p != Locations_read_by_instruction.end(); ++p) { - cerr << p->first << ": " << p->second << '\n'; - } -} -:(before "End One-time Setup") -//? atexit(dump_profile); - -:(code) -void cleanup_main() { - if (!Trace_file.empty() && Trace_stream) { - ofstream fout((Trace_dir+Trace_file).c_str()); - fout << Trace_stream->readable_contents(""); - fout.close(); - } -} -:(before "End One-time Setup") -atexit(cleanup_main); - -:(code) -void load_permanently(string filename) { - if (is_directory(filename)) { - load_all_permanently(filename); - return; - } - ifstream fin(filename.c_str()); - fin.peek(); - if (!fin) { - raise << "no such file " << filename << '\n' << end(); - return; - } - fin >> std::noskipws; - load(fin); - fin.close(); - // freeze everything so it doesn't get cleared by tests - recently_added_recipes.clear(); - // End load_permanently. -} - -bool is_directory(string path) { - struct stat info; - if (stat(path.c_str(), &info)) return false; // error - return info.st_mode & S_IFDIR; -} - -void load_all_permanently(string dir) { - dirent** files; - int num_files = scandir(dir.c_str(), &files, NULL, alphasort); - for (int i = 0; i < num_files; ++i) { - string curr_file = files[i]->d_name; - if (!isdigit(curr_file.at(0))) continue; - load_permanently(dir+'/'+curr_file); - free(files[i]); - files[i] = NULL; - } - free(files); -} -:(before "End Includes") -#include -#include - -//:: Reading from memory, writing to memory. - -:(code) -vector read_memory(reagent x) { - vector result; - if (is_literal(x)) { - result.push_back(x.value); - return result; - } - long long int base = x.value; - long long int size = size_of(x); - for (long long int offset = 0; offset < size; ++offset) { - double val = Memory[base+offset]; - trace(Primitive_recipe_depth, "mem") << "location " << base+offset << " is " << no_scientific(val) << end(); - result.push_back(val); - } - return result; -} - -void write_memory(reagent x, vector data) { - if (is_dummy(x)) return; - if (is_literal(x)) return; - long long int base = x.value; - if (size_mismatch(x, data)) { - raise << current_recipe_name() << ": size mismatch in storing to " << x.original_string << " (" << size_of(x.types) << " vs " << SIZE(data) << ") at '" << current_instruction().to_string() << "'\n" << end(); - return; - } - for (long long int offset = 0; offset < SIZE(data); ++offset) { - trace(Primitive_recipe_depth, "mem") << "storing " << no_scientific(data.at(offset)) << " in location " << base+offset << end(); - Memory[base+offset] = data.at(offset); - } -} - -:(code) -long long int size_of(const reagent& r) { - if (r.types.empty()) return 0; - // End size_of(reagent) Cases - return size_of(r.types); -} -long long int size_of(const vector& types) { - if (types.empty()) return 0; - // End size_of(types) Cases - return 1; -} - -bool size_mismatch(const reagent& x, const vector& data) { - if (x.types.empty()) return true; - // End size_mismatch(x) Cases -//? if (size_of(x) != SIZE(data)) cerr << size_of(x) << " vs " << SIZE(data) << '\n'; - return size_of(x) != SIZE(data); -} - -bool is_dummy(const reagent& x) { - return x.name == "_"; -} - -bool is_literal(const reagent& r) { - return SIZE(r.types) == 1 && r.types.at(0) == 0; -} - -:(code) -// helper for tests -void run(string form) { - vector tmp = load(form); - transform_all(); - if (tmp.empty()) return; - run(tmp.front()); -} - -:(scenario run_label) -recipe main [ - +foo - 1:number <- copy 23 - 2:number <- copy 1:number -] -+run: 1:number <- copy 23 -+run: 2:number <- copy 1:number --run: +foo - -:(scenario run_dummy) -recipe main [ - _ <- copy 0 -] -+run: _ <- copy 0 - -:(scenario write_to_0_disallowed) -recipe main [ - 0 <- copy 34 -] --mem: storing 34 in location 0 - -//: mu is robust to various combinations of commas and spaces. You just have -//: to put spaces around the '<-'. - -:(scenario comma_without_space) -recipe main [ - 1:number, 2:number <- copy 2,2 -] -+mem: storing 2 in location 1 - -:(scenario space_without_comma) -recipe main [ - 1:number, 2:number <- copy 2 2 -] -+mem: storing 2 in location 1 - -:(scenario comma_before_space) -recipe main [ - 1:number, 2:number <- copy 2, 2 -] -+mem: storing 2 in location 1 - -:(scenario comma_after_space) -recipe main [ - 1:number, 2:number <- copy 2 ,2 -] -+mem: storing 2 in location 1 diff --git a/021arithmetic.cc b/021arithmetic.cc deleted file mode 100644 index 107f0853..00000000 --- a/021arithmetic.cc +++ /dev/null @@ -1,248 +0,0 @@ -//: Arithmetic primitives - -:(before "End Primitive Recipe Declarations") -ADD, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["add"] = ADD; -:(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); - products.at(0).push_back(result); - break; -} - -:(scenario add_literal) -recipe main [ - 1:number <- add 23, 34 -] -+mem: storing 57 in location 1 - -:(scenario add) -recipe main [ - 1:number <- copy 23 - 2:number <- copy 34 - 3:number <- add 1:number, 2:number -] -+mem: storing 57 in location 3 - -:(scenario add_multiple) -recipe main [ - 1:number <- add 3, 4, 5 -] -+mem: storing 12 in location 1 - -:(before "End Primitive Recipe Declarations") -SUBTRACT, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["subtract"] = SUBTRACT; -:(before "End Primitive Recipe Implementations") -case SUBTRACT: { - if (ingredients.empty()) { - raise << current_recipe_name() << ": 'subtract' has no ingredients\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(); - break; - } - 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; - } - result -= ingredients.at(i).at(0); - } - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario subtract_literal) -recipe main [ - 1:number <- subtract 5, 2 -] -+mem: storing 3 in location 1 - -:(scenario subtract) -recipe main [ - 1:number <- copy 23 - 2:number <- copy 34 - 3:number <- subtract 1:number, 2:number -] -+mem: storing -11 in location 3 - -:(scenario subtract_multiple) -recipe main [ - 1:number <- subtract 6, 3, 2 -] -+mem: storing 1 in location 1 - -:(before "End Primitive Recipe Declarations") -MULTIPLY, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["multiply"] = MULTIPLY; -:(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); - products.at(0).push_back(result); - break; -} - -:(scenario multiply_literal) -recipe main [ - 1:number <- multiply 2, 3 -] -+mem: storing 6 in location 1 - -:(scenario multiply) -recipe main [ - 1:number <- copy 4 - 2:number <- copy 6 - 3:number <- multiply 1:number, 2:number -] -+mem: storing 24 in location 3 - -:(scenario multiply_multiple) -recipe main [ - 1:number <- multiply 2, 3, 4 -] -+mem: storing 24 in location 1 - -:(before "End Primitive Recipe Declarations") -DIVIDE, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["divide"] = DIVIDE; -:(before "End Primitive Recipe Implementations") -case DIVIDE: { - if (ingredients.empty()) { - raise << current_recipe_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(); - break; - } - 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; - } - result /= ingredients.at(i).at(0); - } - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario divide_literal) -recipe main [ - 1:number <- divide 8, 2 -] -+mem: storing 4 in location 1 - -:(scenario divide) -recipe main [ - 1:number <- copy 27 - 2:number <- copy 3 - 3:number <- divide 1:number, 2:number -] -+mem: storing 9 in location 3 - -:(scenario divide_multiple) -recipe main [ - 1:number <- divide 12, 3, 2 -] -+mem: storing 2 in location 1 - -//: Integer division - -:(before "End Primitive Recipe Declarations") -DIVIDE_WITH_REMAINDER, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["divide-with-remainder"] = DIVIDE_WITH_REMAINDER; -:(before "End Primitive Recipe Implementations") -case DIVIDE_WITH_REMAINDER: { - products.resize(2); - if (SIZE(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))) { - raise << current_recipe_name() << ": 'divide-with-remainder' requires number ingredients, but got '" << current_instruction().to_string() << "'\n" << end(); - break; - } - long long int a = static_cast(ingredients.at(0).at(0)); - long long int b = static_cast(ingredients.at(1).at(0)); - if (b == 0) { - raise << current_recipe_name() << ": divide by zero in '" << current_instruction().to_string() << "'\n" << end(); - break; - } - long long int quotient = a / b; - long long int remainder = a % b; - // very large integers will lose precision - products.at(0).push_back(quotient); - products.at(1).push_back(remainder); - break; -} - -:(scenario divide_with_remainder_literal) -recipe main [ - 1:number, 2:number <- divide-with-remainder 9, 2 -] -+mem: storing 4 in location 1 -+mem: storing 1 in location 2 - -:(scenario divide_with_remainder) -recipe main [ - 1:number <- copy 27 - 2:number <- copy 11 - 3:number, 4:number <- divide-with-remainder 1:number, 2:number -] -+mem: storing 2 in location 3 -+mem: storing 5 in location 4 - -:(scenario divide_with_decimal_point) -recipe main [ - 1:number <- divide 5, 2 -] -+mem: storing 2.5 in location 1 - -:(scenario divide_by_zero) -recipe main [ - 1:number <- divide 4, 0 -] -+mem: storing inf in location 1 - -:(scenario divide_by_zero_2) -% Hide_warnings = true; -recipe main [ - 1:number <- divide-with-remainder 4, 0 -] -# integer division can't return floating-point infinity -+warn: main: divide by zero in '1:number <- divide-with-remainder 4, 0' - -:(code) -inline bool scalar(const vector& x) { - return SIZE(x) == 1; -} -inline bool scalar(const vector& x) { - return SIZE(x) == 1; -} diff --git a/021run.cc b/021run.cc new file mode 100644 index 00000000..514e1204 --- /dev/null +++ b/021run.cc @@ -0,0 +1,358 @@ +//: Phase 3: Start running a loaded and transformed recipe. +//: +//: So far we've seen recipes as lists of instructions, and instructions point +//: at other recipes. To kick things off mu needs to know how to run certain +//: 'primitive' recipes. That will then give the ability to run recipes +//: containing these primitives. +//: +//: This layer defines a skeleton with just two primitive recipes: IDLE which +//: does nothing, and COPY, which can copy numbers from one memory location to +//: another. Later layers will add more primitives. + +:(scenario copy_literal) +recipe main [ + 1:number <- copy 23 +] ++run: 1:number <- copy 23 ++mem: storing 23 in location 1 + +:(scenario copy) +recipe main [ + 1:number <- copy 23 + 2:number <- copy 1:number +] ++run: 2:number <- copy 1:number ++mem: location 1 is 23 ++mem: storing 23 in location 2 + +:(scenario copy_multiple) +recipe main [ + 1:number, 2:number <- copy 23, 24 +] ++mem: storing 23 in location 1 ++mem: storing 24 in location 2 + +:(before "End Types") +// Book-keeping while running a recipe. +//: Later layers will change this. +struct routine { + recipe_ordinal running_recipe; + long long int running_step_index; + routine(recipe_ordinal r) :running_recipe(r), running_step_index(0) {} + bool completed() const; +}; + +:(before "End Globals") +routine* Current_routine = NULL; +map Instructions_running; +map Locations_read; +map Locations_read_by_instruction; + +:(code) +void run(recipe_ordinal r) { + routine rr(r); + Current_routine = &rr; + run_current_routine(); +} + +void run_current_routine() +{ // curly on a separate line, because later layers will modify header + while (!Current_routine->completed()) // later layers will modify condition + { + // Running One Instruction +//? Instructions_running[current_recipe_name()]++; + if (current_instruction().is_label) { ++current_step_index(); continue; } + trace(Initial_callstack_depth+Callstack_depth, "run") << current_instruction().to_string() << end(); + if (Memory[0] != 0) { + raise << "something wrote to location 0; this should never happen\n" << end(); + Memory[0] = 0; + } + // Read all ingredients from memory. + // Each ingredient loads a vector of values rather than a single value; mu + // permits operating on reagents spanning multiple locations. + vector > ingredients; + if (should_copy_ingredients()) { + for (long long int i = 0; i < SIZE(current_instruction().ingredients); ++i) { + ingredients.push_back(read_memory(current_instruction().ingredients.at(i))); +//? Locations_read[current_recipe_name()] += SIZE(ingredients.back()); +//? Locations_read_by_instruction[current_instruction().name] += SIZE(ingredients.back()); + } + } + // Instructions below will write to 'products'. + vector > products; + switch (current_instruction().operation) { + // Primitive Recipe Implementations + case COPY: { + copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin())); + break; + } + // End Primitive Recipe Implementations + default: { + cout << "not a primitive op: " << current_instruction().operation << '\n'; + } + } + finish_instruction: + if (SIZE(products) < SIZE(current_instruction().products)) { + raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string() << end(); + } + else { + for (long long int i = 0; i < SIZE(current_instruction().products); ++i) { + write_memory(current_instruction().products.at(i), products.at(i)); + } + } + // End of Instruction + ++current_step_index(); + } + stop_running_current_routine:; +} + +bool should_copy_ingredients() { + // End should_copy_ingredients Special-cases + return true; +} + +//: Some helpers. +//: We'll need to override these later as we change the definition of routine. +//: Important that they return referrences into the routine. + +inline long long int& current_step_index() { + return Current_routine->running_step_index; +} + +inline const string& current_recipe_name() { + return Recipe[Current_routine->running_recipe].name; +} + +inline const instruction& current_instruction() { + return Recipe[Current_routine->running_recipe].steps.at(Current_routine->running_step_index); +} + +inline bool routine::completed() const { + return running_step_index >= SIZE(Recipe[running_recipe].steps); +} + +//:: Startup flow + +//: Step 1: load all .mu files with numeric prefixes (in order) +:(before "End Load Recipes") +load_permanently("core.mu"); +transform_all(); + +//: Step 2: load any .mu files provided at the commandline +:(before "End Commandline Parsing") +if (argc > 1) { + // skip argv[0] + argv++; + argc--; + // ignore argv past '--'; that's commandline args for 'main' + while (argc > 0) { + if (string(*argv) == "--") break; + load_permanently(*argv); + argv++; + argc--; + } + transform_all(); + if (Run_tests) Recipe.erase(Recipe_ordinal[string("main")]); +} + +//: Step 3: if we aren't running tests, locate a recipe called 'main' and +//: start running it. +:(before "End Main") +if (!Run_tests) { + setup(); +//? Trace_file = "interactive"; +//? START_TRACING_UNTIL_END_OF_SCOPE; +//? Trace_stream->collect_layers.insert("app"); + run_main(argc, argv); + teardown(); +} + +:(code) +void run_main(int argc, char* argv[]) { + recipe_ordinal r = Recipe_ordinal[string("main")]; + if (r) run(r); +} + +:(code) +void dump_profile() { + for (map::iterator p = Instructions_running.begin(); p != Instructions_running.end(); ++p) { + cerr << p->first << ": " << p->second << '\n'; + } + cerr << "== locations read\n"; + for (map::iterator p = Locations_read.begin(); p != Locations_read.end(); ++p) { + cerr << p->first << ": " << p->second << '\n'; + } + cerr << "== locations read by instruction\n"; + for (map::iterator p = Locations_read_by_instruction.begin(); p != Locations_read_by_instruction.end(); ++p) { + cerr << p->first << ": " << p->second << '\n'; + } +} +:(before "End One-time Setup") +//? atexit(dump_profile); + +:(code) +void cleanup_main() { + if (!Trace_file.empty() && Trace_stream) { + ofstream fout((Trace_dir+Trace_file).c_str()); + fout << Trace_stream->readable_contents(""); + fout.close(); + } +} +:(before "End One-time Setup") +atexit(cleanup_main); + +:(code) +void load_permanently(string filename) { + if (is_directory(filename)) { + load_all_permanently(filename); + return; + } + ifstream fin(filename.c_str()); + fin.peek(); + if (!fin) { + raise << "no such file " << filename << '\n' << end(); + return; + } + fin >> std::noskipws; + load(fin); + fin.close(); + // freeze everything so it doesn't get cleared by tests + recently_added_recipes.clear(); + // End load_permanently. +} + +bool is_directory(string path) { + struct stat info; + if (stat(path.c_str(), &info)) return false; // error + return info.st_mode & S_IFDIR; +} + +void load_all_permanently(string dir) { + dirent** files; + int num_files = scandir(dir.c_str(), &files, NULL, alphasort); + for (int i = 0; i < num_files; ++i) { + string curr_file = files[i]->d_name; + if (!isdigit(curr_file.at(0))) continue; + load_permanently(dir+'/'+curr_file); + free(files[i]); + files[i] = NULL; + } + free(files); +} +:(before "End Includes") +#include +#include + +//:: Reading from memory, writing to memory. + +:(code) +vector read_memory(reagent x) { + vector result; + if (is_literal(x)) { + result.push_back(x.value); + return result; + } + long long int base = x.value; + long long int size = size_of(x); + for (long long int offset = 0; offset < size; ++offset) { + double val = Memory[base+offset]; + trace(Primitive_recipe_depth, "mem") << "location " << base+offset << " is " << no_scientific(val) << end(); + result.push_back(val); + } + return result; +} + +void write_memory(reagent x, vector data) { + if (is_dummy(x)) return; + if (is_literal(x)) return; + long long int base = x.value; + if (size_mismatch(x, data)) { + raise << current_recipe_name() << ": size mismatch in storing to " << x.original_string << " (" << size_of(x.types) << " vs " << SIZE(data) << ") at '" << current_instruction().to_string() << "'\n" << end(); + return; + } + for (long long int offset = 0; offset < SIZE(data); ++offset) { + trace(Primitive_recipe_depth, "mem") << "storing " << no_scientific(data.at(offset)) << " in location " << base+offset << end(); + Memory[base+offset] = data.at(offset); + } +} + +:(code) +long long int size_of(const reagent& r) { + if (r.types.empty()) return 0; + // End size_of(reagent) Cases + return size_of(r.types); +} +long long int size_of(const vector& types) { + if (types.empty()) return 0; + // End size_of(types) Cases + return 1; +} + +bool size_mismatch(const reagent& x, const vector& data) { + if (x.types.empty()) return true; + // End size_mismatch(x) Cases +//? if (size_of(x) != SIZE(data)) cerr << size_of(x) << " vs " << SIZE(data) << '\n'; + return size_of(x) != SIZE(data); +} + +bool is_dummy(const reagent& x) { + return x.name == "_"; +} + +:(replace{} "void run(string form)") +// helper for tests +void run(string form) { + vector tmp = load(form); + transform_all(); + if (tmp.empty()) return; + run(tmp.front()); +} + +:(scenario run_label) +recipe main [ + +foo + 1:number <- copy 23 + 2:number <- copy 1:number +] ++run: 1:number <- copy 23 ++run: 2:number <- copy 1:number +-run: +foo + +:(scenario run_dummy) +recipe main [ + _ <- copy 0 +] ++run: _ <- copy 0 + +:(scenario write_to_0_disallowed) +recipe main [ + 0 <- copy 34 +] +-mem: storing 34 in location 0 + +//: mu is robust to various combinations of commas and spaces. You just have +//: to put spaces around the '<-'. + +:(scenario comma_without_space) +recipe main [ + 1:number, 2:number <- copy 2,2 +] ++mem: storing 2 in location 1 + +:(scenario space_without_comma) +recipe main [ + 1:number, 2:number <- copy 2 2 +] ++mem: storing 2 in location 1 + +:(scenario comma_before_space) +recipe main [ + 1:number, 2:number <- copy 2, 2 +] ++mem: storing 2 in location 1 + +:(scenario comma_after_space) +recipe main [ + 1:number, 2:number <- copy 2 ,2 +] ++mem: storing 2 in location 1 diff --git a/022arithmetic.cc b/022arithmetic.cc new file mode 100644 index 00000000..107f0853 --- /dev/null +++ b/022arithmetic.cc @@ -0,0 +1,248 @@ +//: Arithmetic primitives + +:(before "End Primitive Recipe Declarations") +ADD, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["add"] = ADD; +:(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); + products.at(0).push_back(result); + break; +} + +:(scenario add_literal) +recipe main [ + 1:number <- add 23, 34 +] ++mem: storing 57 in location 1 + +:(scenario add) +recipe main [ + 1:number <- copy 23 + 2:number <- copy 34 + 3:number <- add 1:number, 2:number +] ++mem: storing 57 in location 3 + +:(scenario add_multiple) +recipe main [ + 1:number <- add 3, 4, 5 +] ++mem: storing 12 in location 1 + +:(before "End Primitive Recipe Declarations") +SUBTRACT, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["subtract"] = SUBTRACT; +:(before "End Primitive Recipe Implementations") +case SUBTRACT: { + if (ingredients.empty()) { + raise << current_recipe_name() << ": 'subtract' has no ingredients\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(); + break; + } + 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; + } + result -= ingredients.at(i).at(0); + } + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario subtract_literal) +recipe main [ + 1:number <- subtract 5, 2 +] ++mem: storing 3 in location 1 + +:(scenario subtract) +recipe main [ + 1:number <- copy 23 + 2:number <- copy 34 + 3:number <- subtract 1:number, 2:number +] ++mem: storing -11 in location 3 + +:(scenario subtract_multiple) +recipe main [ + 1:number <- subtract 6, 3, 2 +] ++mem: storing 1 in location 1 + +:(before "End Primitive Recipe Declarations") +MULTIPLY, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["multiply"] = MULTIPLY; +:(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); + products.at(0).push_back(result); + break; +} + +:(scenario multiply_literal) +recipe main [ + 1:number <- multiply 2, 3 +] ++mem: storing 6 in location 1 + +:(scenario multiply) +recipe main [ + 1:number <- copy 4 + 2:number <- copy 6 + 3:number <- multiply 1:number, 2:number +] ++mem: storing 24 in location 3 + +:(scenario multiply_multiple) +recipe main [ + 1:number <- multiply 2, 3, 4 +] ++mem: storing 24 in location 1 + +:(before "End Primitive Recipe Declarations") +DIVIDE, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["divide"] = DIVIDE; +:(before "End Primitive Recipe Implementations") +case DIVIDE: { + if (ingredients.empty()) { + raise << current_recipe_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(); + break; + } + 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; + } + result /= ingredients.at(i).at(0); + } + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario divide_literal) +recipe main [ + 1:number <- divide 8, 2 +] ++mem: storing 4 in location 1 + +:(scenario divide) +recipe main [ + 1:number <- copy 27 + 2:number <- copy 3 + 3:number <- divide 1:number, 2:number +] ++mem: storing 9 in location 3 + +:(scenario divide_multiple) +recipe main [ + 1:number <- divide 12, 3, 2 +] ++mem: storing 2 in location 1 + +//: Integer division + +:(before "End Primitive Recipe Declarations") +DIVIDE_WITH_REMAINDER, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["divide-with-remainder"] = DIVIDE_WITH_REMAINDER; +:(before "End Primitive Recipe Implementations") +case DIVIDE_WITH_REMAINDER: { + products.resize(2); + if (SIZE(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))) { + raise << current_recipe_name() << ": 'divide-with-remainder' requires number ingredients, but got '" << current_instruction().to_string() << "'\n" << end(); + break; + } + long long int a = static_cast(ingredients.at(0).at(0)); + long long int b = static_cast(ingredients.at(1).at(0)); + if (b == 0) { + raise << current_recipe_name() << ": divide by zero in '" << current_instruction().to_string() << "'\n" << end(); + break; + } + long long int quotient = a / b; + long long int remainder = a % b; + // very large integers will lose precision + products.at(0).push_back(quotient); + products.at(1).push_back(remainder); + break; +} + +:(scenario divide_with_remainder_literal) +recipe main [ + 1:number, 2:number <- divide-with-remainder 9, 2 +] ++mem: storing 4 in location 1 ++mem: storing 1 in location 2 + +:(scenario divide_with_remainder) +recipe main [ + 1:number <- copy 27 + 2:number <- copy 11 + 3:number, 4:number <- divide-with-remainder 1:number, 2:number +] ++mem: storing 2 in location 3 ++mem: storing 5 in location 4 + +:(scenario divide_with_decimal_point) +recipe main [ + 1:number <- divide 5, 2 +] ++mem: storing 2.5 in location 1 + +:(scenario divide_by_zero) +recipe main [ + 1:number <- divide 4, 0 +] ++mem: storing inf in location 1 + +:(scenario divide_by_zero_2) +% Hide_warnings = true; +recipe main [ + 1:number <- divide-with-remainder 4, 0 +] +# integer division can't return floating-point infinity ++warn: main: divide by zero in '1:number <- divide-with-remainder 4, 0' + +:(code) +inline bool scalar(const vector& x) { + return SIZE(x) == 1; +} +inline bool scalar(const vector& x) { + return SIZE(x) == 1; +} diff --git a/022boolean.cc b/022boolean.cc deleted file mode 100644 index ead678cf..00000000 --- a/022boolean.cc +++ /dev/null @@ -1,123 +0,0 @@ -//: Boolean primitives - -:(before "End Primitive Recipe Declarations") -AND, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["and"] = AND; -:(before "End Primitive Recipe Implementations") -case AND: { - bool result = true; - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'and' requires boolean ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } - result = result && ingredients.at(i).at(0); - } - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario and) -recipe main [ - 1:boolean <- copy 1 - 2:boolean <- copy 0 - 3:boolean <- and 1:boolean, 2:boolean -] -+mem: storing 0 in location 3 - -:(scenario and_2) -recipe main [ - 1:boolean <- and 1, 1 -] -+mem: storing 1 in location 1 - -:(scenario and_multiple) -recipe main [ - 1:boolean <- and 1, 1, 0 -] -+mem: storing 0 in location 1 - -:(scenario and_multiple_2) -recipe main [ - 1:boolean <- and 1, 1, 1 -] -+mem: storing 1 in location 1 - -:(before "End Primitive Recipe Declarations") -OR, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["or"] = OR; -:(before "End Primitive Recipe Implementations") -case OR: { - bool result = false; - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'or' requires boolean ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } - result = result || ingredients.at(i).at(0); - } - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario or) -recipe main [ - 1:boolean <- copy 1 - 2:boolean <- copy 0 - 3:boolean <- or 1:boolean, 2:boolean -] -+mem: storing 1 in location 3 - -:(scenario or_2) -recipe main [ - 1:boolean <- or 0, 0 -] -+mem: storing 0 in location 1 - -:(scenario or_multiple) -recipe main [ - 1:boolean <- and 0, 0, 0 -] -+mem: storing 0 in location 1 - -:(scenario or_multiple_2) -recipe main [ - 1:boolean <- or 0, 0, 1 -] -+mem: storing 1 in location 1 - -:(before "End Primitive Recipe Declarations") -NOT, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["not"] = NOT; -:(before "End Primitive Recipe Implementations") -case NOT: { - products.resize(SIZE(ingredients)); - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'not' requires boolean ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_instruction; - } - products.at(i).push_back(!ingredients.at(i).at(0)); - } - break; -} - -:(scenario not) -recipe main [ - 1:boolean <- copy 1 - 2:boolean <- not 1:boolean -] -+mem: storing 0 in location 2 - -:(scenario not_multiple) -recipe main [ - 1:boolean, 2:boolean, 3:boolean <- not 1, 0, 1 -] -+mem: storing 0 in location 1 -+mem: storing 1 in location 2 -+mem: storing 0 in location 3 diff --git a/023boolean.cc b/023boolean.cc new file mode 100644 index 00000000..ead678cf --- /dev/null +++ b/023boolean.cc @@ -0,0 +1,123 @@ +//: Boolean primitives + +:(before "End Primitive Recipe Declarations") +AND, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["and"] = AND; +:(before "End Primitive Recipe Implementations") +case AND: { + bool result = true; + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'and' requires boolean ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_instruction; + } + result = result && ingredients.at(i).at(0); + } + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario and) +recipe main [ + 1:boolean <- copy 1 + 2:boolean <- copy 0 + 3:boolean <- and 1:boolean, 2:boolean +] ++mem: storing 0 in location 3 + +:(scenario and_2) +recipe main [ + 1:boolean <- and 1, 1 +] ++mem: storing 1 in location 1 + +:(scenario and_multiple) +recipe main [ + 1:boolean <- and 1, 1, 0 +] ++mem: storing 0 in location 1 + +:(scenario and_multiple_2) +recipe main [ + 1:boolean <- and 1, 1, 1 +] ++mem: storing 1 in location 1 + +:(before "End Primitive Recipe Declarations") +OR, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["or"] = OR; +:(before "End Primitive Recipe Implementations") +case OR: { + bool result = false; + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'or' requires boolean ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_instruction; + } + result = result || ingredients.at(i).at(0); + } + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario or) +recipe main [ + 1:boolean <- copy 1 + 2:boolean <- copy 0 + 3:boolean <- or 1:boolean, 2:boolean +] ++mem: storing 1 in location 3 + +:(scenario or_2) +recipe main [ + 1:boolean <- or 0, 0 +] ++mem: storing 0 in location 1 + +:(scenario or_multiple) +recipe main [ + 1:boolean <- and 0, 0, 0 +] ++mem: storing 0 in location 1 + +:(scenario or_multiple_2) +recipe main [ + 1:boolean <- or 0, 0, 1 +] ++mem: storing 1 in location 1 + +:(before "End Primitive Recipe Declarations") +NOT, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["not"] = NOT; +:(before "End Primitive Recipe Implementations") +case NOT: { + products.resize(SIZE(ingredients)); + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'not' requires boolean ingredients, but got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_instruction; + } + products.at(i).push_back(!ingredients.at(i).at(0)); + } + break; +} + +:(scenario not) +recipe main [ + 1:boolean <- copy 1 + 2:boolean <- not 1:boolean +] ++mem: storing 0 in location 2 + +:(scenario not_multiple) +recipe main [ + 1:boolean, 2:boolean, 3:boolean <- not 1, 0, 1 +] ++mem: storing 0 in location 1 ++mem: storing 1 in location 2 ++mem: storing 0 in location 3 diff --git a/023jump.cc b/023jump.cc deleted file mode 100644 index 925906de..00000000 --- a/023jump.cc +++ /dev/null @@ -1,141 +0,0 @@ -//: Jump primitives - -:(scenario jump_can_skip_instructions) -recipe main [ - jump 1:offset - 1:number <- copy 1 -] -+run: jump 1:offset --run: 1:number <- copy 1 --mem: storing 1 in location 1 - -:(before "End Primitive Recipe Declarations") -JUMP, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["jump"] = JUMP; -:(before "End Primitive Recipe Implementations") -case JUMP: { - if (SIZE(ingredients) != 1) { - raise << current_recipe_name() << ": 'jump' requires exactly one ingredient, but got " << current_instruction().to_string() << '\n' << end(); - break; - } - if (!scalar(ingredients.at(0))) { - raise << current_recipe_name() << ": first ingredient of 'jump' should be a label or offset, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - break; - } - assert(current_instruction().ingredients.at(0).initialized); - current_step_index() += ingredients.at(0).at(0)+1; - trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end(); - continue; // skip rest of this instruction -} - -//: special type to designate jump targets -:(before "End Mu Types Initialization") -Type_ordinal["offset"] = 0; - -:(scenario jump_backward) -recipe main [ - jump 1:offset # 0 -+ - jump 3:offset # | +-+ 1 - # \/ /\ | - jump -2:offset # 2 +-->+ | -] # \/ 3 -+run: jump 1:offset -+run: jump -2:offset -+run: jump 3:offset - -:(before "End Primitive Recipe Declarations") -JUMP_IF, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["jump-if"] = JUMP_IF; -:(before "End Primitive Recipe Implementations") -case JUMP_IF: { - if (SIZE(ingredients) != 2) { - raise << current_recipe_name() << ": 'jump-if' requires exactly two ingredients, but got " << current_instruction().to_string() << '\n' << end(); - break; - } - if (!scalar(ingredients.at(0))) { - raise << current_recipe_name() << ": 'jump-if' requires a boolean for its first ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - break; - } - if (!scalar(ingredients.at(1))) { - raise << current_recipe_name() << ": 'jump-if' requires a label or offset for its second ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - break; - } - assert(current_instruction().ingredients.at(1).initialized); - if (!ingredients.at(0).at(0)) { - trace(Primitive_recipe_depth, "run") << "jump-if fell through" << end(); - break; - } - current_step_index() += ingredients.at(1).at(0)+1; - trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end(); - continue; // skip rest of this instruction -} - -:(scenario jump_if) -recipe main [ - jump-if 999, 1:offset - 123:number <- copy 1 -] -+run: jump-if 999, 1:offset -+run: jumping to instruction 2 --run: 1:number <- copy 1 --mem: storing 1 in location 123 - -:(scenario jump_if_fallthrough) -recipe main [ - jump-if 0, 1:offset - 123:number <- copy 1 -] -+run: jump-if 0, 1:offset -+run: jump-if fell through -+run: 123:number <- copy 1 -+mem: storing 1 in location 123 - -:(before "End Primitive Recipe Declarations") -JUMP_UNLESS, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["jump-unless"] = JUMP_UNLESS; -:(before "End Primitive Recipe Implementations") -case JUMP_UNLESS: { - if (SIZE(ingredients) != 2) { - raise << current_recipe_name() << ": 'jump-unless' requires exactly two ingredients, but got " << current_instruction().to_string() << '\n' << end(); - break; - } - if (!scalar(ingredients.at(0))) { - raise << current_recipe_name() << ": 'jump-unless' requires a boolean for its first ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - break; - } - if (!scalar(ingredients.at(1))) { - raise << current_recipe_name() << ": 'jump-unless' requires a label or offset for its second ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - break; - } - assert(current_instruction().ingredients.at(1).initialized); - if (ingredients.at(0).at(0)) { - trace(Primitive_recipe_depth, "run") << "jump-unless fell through" << end(); - break; - } - current_step_index() += ingredients.at(1).at(0)+1; - trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end(); - continue; // skip rest of this instruction -} - -:(scenario jump_unless) -recipe main [ - jump-unless 0, 1:offset - 123:number <- copy 1 -] -+run: jump-unless 0, 1:offset -+run: jumping to instruction 2 --run: 123:number <- copy 1 --mem: storing 1 in location 123 - -:(scenario jump_unless_fallthrough) -recipe main [ - jump-unless 999, 1:offset - 123:number <- copy 1 -] -+run: jump-unless 999, 1:offset -+run: jump-unless fell through -+run: 123:number <- copy 1 -+mem: storing 1 in location 123 diff --git a/024compare.cc b/024compare.cc deleted file mode 100644 index 619caa03..00000000 --- a/024compare.cc +++ /dev/null @@ -1,296 +0,0 @@ -//: Comparison primitives - -:(before "End Primitive Recipe Declarations") -EQUAL, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["equal"] = EQUAL; -:(before "End Primitive Recipe Implementations") -case EQUAL: { - if (SIZE(ingredients) <= 1) { - raise << current_recipe_name() << ": 'equal' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); - break; - } - vector& exemplar = ingredients.at(0); - bool result = true; - for (long long int i = 1; i < SIZE(ingredients); ++i) { - if (!equal(ingredients.at(i).begin(), ingredients.at(i).end(), exemplar.begin())) { - result = false; - break; - } - } - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario equal) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 33 - 3:number <- equal 1:number, 2:number -] -+mem: location 1 is 34 -+mem: location 2 is 33 -+mem: storing 0 in location 3 - -:(scenario equal_2) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 34 - 3:number <- equal 1:number, 2:number -] -+mem: location 1 is 34 -+mem: location 2 is 34 -+mem: storing 1 in location 3 - -:(scenario equal_multiple) -recipe main [ - 1:number <- equal 34, 34, 34 -] -+mem: storing 1 in location 1 - -:(scenario equal_multiple_2) -recipe main [ - 1:number <- equal 34, 34, 35 -] -+mem: storing 0 in location 1 - -:(before "End Primitive Recipe Declarations") -GREATER_THAN, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["greater-than"] = GREATER_THAN; -:(before "End Primitive Recipe Implementations") -case GREATER_THAN: { - bool result = true; - if (SIZE(ingredients) <= 1) { - raise << current_recipe_name() << ": 'greater-than' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); - break; - } - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'greater-than' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_greater_than; - } - } - for (long long int i = /**/1; i < SIZE(ingredients); ++i) { - if (ingredients.at(i-1).at(0) <= ingredients.at(i).at(0)) { - result = false; - } - } - finish_greater_than: - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario greater_than) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 33 - 3:boolean <- greater-than 1:number, 2:number -] -+mem: storing 1 in location 3 - -:(scenario greater_than_2) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 34 - 3:boolean <- greater-than 1:number, 2:number -] -+mem: storing 0 in location 3 - -:(scenario greater_than_multiple) -recipe main [ - 1:boolean <- greater-than 36, 35, 34 -] -+mem: storing 1 in location 1 - -:(scenario greater_than_multiple_2) -recipe main [ - 1:boolean <- greater-than 36, 35, 35 -] -+mem: storing 0 in location 1 - -:(before "End Primitive Recipe Declarations") -LESSER_THAN, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["lesser-than"] = LESSER_THAN; -:(before "End Primitive Recipe Implementations") -case LESSER_THAN: { - bool result = true; - if (SIZE(ingredients) <= 1) { - raise << current_recipe_name() << ": 'lesser-than' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); - break; - } - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'lesser-than' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_lesser_than; - } - } - for (long long int i = /**/1; i < SIZE(ingredients); ++i) { - if (ingredients.at(i-1).at(0) >= ingredients.at(i).at(0)) { - result = false; - } - } - finish_lesser_than: - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario lesser_than) -recipe main [ - 1:number <- copy 32 - 2:number <- copy 33 - 3:boolean <- lesser-than 1:number, 2:number -] -+mem: storing 1 in location 3 - -:(scenario lesser_than_2) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 33 - 3:boolean <- lesser-than 1:number, 2:number -] -+mem: storing 0 in location 3 - -:(scenario lesser_than_multiple) -recipe main [ - 1:boolean <- lesser-than 34, 35, 36 -] -+mem: storing 1 in location 1 - -:(scenario lesser_than_multiple_2) -recipe main [ - 1:boolean <- lesser-than 34, 35, 35 -] -+mem: storing 0 in location 1 - -:(before "End Primitive Recipe Declarations") -GREATER_OR_EQUAL, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["greater-or-equal"] = GREATER_OR_EQUAL; -:(before "End Primitive Recipe Implementations") -case GREATER_OR_EQUAL: { - bool result = true; - if (SIZE(ingredients) <= 1) { - raise << current_recipe_name() << ": 'greater-or-equal' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); - break; - } - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'greater-or-equal' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_greater_or_equal; - } - } - for (long long int i = /**/1; i < SIZE(ingredients); ++i) { - if (ingredients.at(i-1).at(0) < ingredients.at(i).at(0)) { - result = false; - } - } - finish_greater_or_equal: - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario greater_or_equal) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 33 - 3:boolean <- greater-or-equal 1:number, 2:number -] -+mem: storing 1 in location 3 - -:(scenario greater_or_equal_2) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 34 - 3:boolean <- greater-or-equal 1:number, 2:number -] -+mem: storing 1 in location 3 - -:(scenario greater_or_equal_3) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 35 - 3:boolean <- greater-or-equal 1:number, 2:number -] -+mem: storing 0 in location 3 - -:(scenario greater_or_equal_multiple) -recipe main [ - 1:boolean <- greater-or-equal 36, 35, 35 -] -+mem: storing 1 in location 1 - -:(scenario greater_or_equal_multiple_2) -recipe main [ - 1:boolean <- greater-or-equal 36, 35, 36 -] -+mem: storing 0 in location 1 - -:(before "End Primitive Recipe Declarations") -LESSER_OR_EQUAL, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["lesser-or-equal"] = LESSER_OR_EQUAL; -:(before "End Primitive Recipe Implementations") -case LESSER_OR_EQUAL: { - bool result = true; - if (SIZE(ingredients) <= 1) { - raise << current_recipe_name() << ": 'lesser-or-equal' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); - break; - } - for (long long int i = 0; i < SIZE(ingredients); ++i) { - if (!scalar(ingredients.at(i))) { - raise << current_recipe_name() << ": 'lesser-or-equal' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); - goto finish_lesser_or_equal; - } - } - for (long long int i = /**/1; i < SIZE(ingredients); ++i) { - if (ingredients.at(i-1).at(0) > ingredients.at(i).at(0)) { - result = false; - } - } - finish_lesser_or_equal: - products.resize(1); - products.at(0).push_back(result); - break; -} - -:(scenario lesser_or_equal) -recipe main [ - 1:number <- copy 32 - 2:number <- copy 33 - 3:boolean <- lesser-or-equal 1:number, 2:number -] -+mem: storing 1 in location 3 - -:(scenario lesser_or_equal_2) -recipe main [ - 1:number <- copy 33 - 2:number <- copy 33 - 3:boolean <- lesser-or-equal 1:number, 2:number -] -+mem: storing 1 in location 3 - -:(scenario lesser_or_equal_3) -recipe main [ - 1:number <- copy 34 - 2:number <- copy 33 - 3:boolean <- lesser-or-equal 1:number, 2:number -] -+mem: storing 0 in location 3 - -:(scenario lesser_or_equal_multiple) -recipe main [ - 1:boolean <- lesser-or-equal 34, 35, 35 -] -+mem: storing 1 in location 1 - -:(scenario lesser_or_equal_multiple_2) -recipe main [ - 1:boolean <- lesser-or-equal 34, 35, 34 -] -+mem: storing 0 in location 1 diff --git a/024jump.cc b/024jump.cc new file mode 100644 index 00000000..925906de --- /dev/null +++ b/024jump.cc @@ -0,0 +1,141 @@ +//: Jump primitives + +:(scenario jump_can_skip_instructions) +recipe main [ + jump 1:offset + 1:number <- copy 1 +] ++run: jump 1:offset +-run: 1:number <- copy 1 +-mem: storing 1 in location 1 + +:(before "End Primitive Recipe Declarations") +JUMP, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["jump"] = JUMP; +:(before "End Primitive Recipe Implementations") +case JUMP: { + if (SIZE(ingredients) != 1) { + raise << current_recipe_name() << ": 'jump' requires exactly one ingredient, but got " << current_instruction().to_string() << '\n' << end(); + break; + } + if (!scalar(ingredients.at(0))) { + raise << current_recipe_name() << ": first ingredient of 'jump' should be a label or offset, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + break; + } + assert(current_instruction().ingredients.at(0).initialized); + current_step_index() += ingredients.at(0).at(0)+1; + trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end(); + continue; // skip rest of this instruction +} + +//: special type to designate jump targets +:(before "End Mu Types Initialization") +Type_ordinal["offset"] = 0; + +:(scenario jump_backward) +recipe main [ + jump 1:offset # 0 -+ + jump 3:offset # | +-+ 1 + # \/ /\ | + jump -2:offset # 2 +-->+ | +] # \/ 3 ++run: jump 1:offset ++run: jump -2:offset ++run: jump 3:offset + +:(before "End Primitive Recipe Declarations") +JUMP_IF, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["jump-if"] = JUMP_IF; +:(before "End Primitive Recipe Implementations") +case JUMP_IF: { + if (SIZE(ingredients) != 2) { + raise << current_recipe_name() << ": 'jump-if' requires exactly two ingredients, but got " << current_instruction().to_string() << '\n' << end(); + break; + } + if (!scalar(ingredients.at(0))) { + raise << current_recipe_name() << ": 'jump-if' requires a boolean for its first ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + break; + } + if (!scalar(ingredients.at(1))) { + raise << current_recipe_name() << ": 'jump-if' requires a label or offset for its second ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + break; + } + assert(current_instruction().ingredients.at(1).initialized); + if (!ingredients.at(0).at(0)) { + trace(Primitive_recipe_depth, "run") << "jump-if fell through" << end(); + break; + } + current_step_index() += ingredients.at(1).at(0)+1; + trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end(); + continue; // skip rest of this instruction +} + +:(scenario jump_if) +recipe main [ + jump-if 999, 1:offset + 123:number <- copy 1 +] ++run: jump-if 999, 1:offset ++run: jumping to instruction 2 +-run: 1:number <- copy 1 +-mem: storing 1 in location 123 + +:(scenario jump_if_fallthrough) +recipe main [ + jump-if 0, 1:offset + 123:number <- copy 1 +] ++run: jump-if 0, 1:offset ++run: jump-if fell through ++run: 123:number <- copy 1 ++mem: storing 1 in location 123 + +:(before "End Primitive Recipe Declarations") +JUMP_UNLESS, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["jump-unless"] = JUMP_UNLESS; +:(before "End Primitive Recipe Implementations") +case JUMP_UNLESS: { + if (SIZE(ingredients) != 2) { + raise << current_recipe_name() << ": 'jump-unless' requires exactly two ingredients, but got " << current_instruction().to_string() << '\n' << end(); + break; + } + if (!scalar(ingredients.at(0))) { + raise << current_recipe_name() << ": 'jump-unless' requires a boolean for its first ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + break; + } + if (!scalar(ingredients.at(1))) { + raise << current_recipe_name() << ": 'jump-unless' requires a label or offset for its second ingredient, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + break; + } + assert(current_instruction().ingredients.at(1).initialized); + if (ingredients.at(0).at(0)) { + trace(Primitive_recipe_depth, "run") << "jump-unless fell through" << end(); + break; + } + current_step_index() += ingredients.at(1).at(0)+1; + trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end(); + continue; // skip rest of this instruction +} + +:(scenario jump_unless) +recipe main [ + jump-unless 0, 1:offset + 123:number <- copy 1 +] ++run: jump-unless 0, 1:offset ++run: jumping to instruction 2 +-run: 123:number <- copy 1 +-mem: storing 1 in location 123 + +:(scenario jump_unless_fallthrough) +recipe main [ + jump-unless 999, 1:offset + 123:number <- copy 1 +] ++run: jump-unless 999, 1:offset ++run: jump-unless fell through ++run: 123:number <- copy 1 ++mem: storing 1 in location 123 diff --git a/025compare.cc b/025compare.cc new file mode 100644 index 00000000..619caa03 --- /dev/null +++ b/025compare.cc @@ -0,0 +1,296 @@ +//: Comparison primitives + +:(before "End Primitive Recipe Declarations") +EQUAL, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["equal"] = EQUAL; +:(before "End Primitive Recipe Implementations") +case EQUAL: { + if (SIZE(ingredients) <= 1) { + raise << current_recipe_name() << ": 'equal' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); + break; + } + vector& exemplar = ingredients.at(0); + bool result = true; + for (long long int i = 1; i < SIZE(ingredients); ++i) { + if (!equal(ingredients.at(i).begin(), ingredients.at(i).end(), exemplar.begin())) { + result = false; + break; + } + } + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario equal) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 33 + 3:number <- equal 1:number, 2:number +] ++mem: location 1 is 34 ++mem: location 2 is 33 ++mem: storing 0 in location 3 + +:(scenario equal_2) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 34 + 3:number <- equal 1:number, 2:number +] ++mem: location 1 is 34 ++mem: location 2 is 34 ++mem: storing 1 in location 3 + +:(scenario equal_multiple) +recipe main [ + 1:number <- equal 34, 34, 34 +] ++mem: storing 1 in location 1 + +:(scenario equal_multiple_2) +recipe main [ + 1:number <- equal 34, 34, 35 +] ++mem: storing 0 in location 1 + +:(before "End Primitive Recipe Declarations") +GREATER_THAN, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["greater-than"] = GREATER_THAN; +:(before "End Primitive Recipe Implementations") +case GREATER_THAN: { + bool result = true; + if (SIZE(ingredients) <= 1) { + raise << current_recipe_name() << ": 'greater-than' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); + break; + } + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'greater-than' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_greater_than; + } + } + for (long long int i = /**/1; i < SIZE(ingredients); ++i) { + if (ingredients.at(i-1).at(0) <= ingredients.at(i).at(0)) { + result = false; + } + } + finish_greater_than: + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario greater_than) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 33 + 3:boolean <- greater-than 1:number, 2:number +] ++mem: storing 1 in location 3 + +:(scenario greater_than_2) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 34 + 3:boolean <- greater-than 1:number, 2:number +] ++mem: storing 0 in location 3 + +:(scenario greater_than_multiple) +recipe main [ + 1:boolean <- greater-than 36, 35, 34 +] ++mem: storing 1 in location 1 + +:(scenario greater_than_multiple_2) +recipe main [ + 1:boolean <- greater-than 36, 35, 35 +] ++mem: storing 0 in location 1 + +:(before "End Primitive Recipe Declarations") +LESSER_THAN, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["lesser-than"] = LESSER_THAN; +:(before "End Primitive Recipe Implementations") +case LESSER_THAN: { + bool result = true; + if (SIZE(ingredients) <= 1) { + raise << current_recipe_name() << ": 'lesser-than' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); + break; + } + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'lesser-than' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_lesser_than; + } + } + for (long long int i = /**/1; i < SIZE(ingredients); ++i) { + if (ingredients.at(i-1).at(0) >= ingredients.at(i).at(0)) { + result = false; + } + } + finish_lesser_than: + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario lesser_than) +recipe main [ + 1:number <- copy 32 + 2:number <- copy 33 + 3:boolean <- lesser-than 1:number, 2:number +] ++mem: storing 1 in location 3 + +:(scenario lesser_than_2) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 33 + 3:boolean <- lesser-than 1:number, 2:number +] ++mem: storing 0 in location 3 + +:(scenario lesser_than_multiple) +recipe main [ + 1:boolean <- lesser-than 34, 35, 36 +] ++mem: storing 1 in location 1 + +:(scenario lesser_than_multiple_2) +recipe main [ + 1:boolean <- lesser-than 34, 35, 35 +] ++mem: storing 0 in location 1 + +:(before "End Primitive Recipe Declarations") +GREATER_OR_EQUAL, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["greater-or-equal"] = GREATER_OR_EQUAL; +:(before "End Primitive Recipe Implementations") +case GREATER_OR_EQUAL: { + bool result = true; + if (SIZE(ingredients) <= 1) { + raise << current_recipe_name() << ": 'greater-or-equal' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); + break; + } + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'greater-or-equal' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_greater_or_equal; + } + } + for (long long int i = /**/1; i < SIZE(ingredients); ++i) { + if (ingredients.at(i-1).at(0) < ingredients.at(i).at(0)) { + result = false; + } + } + finish_greater_or_equal: + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario greater_or_equal) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 33 + 3:boolean <- greater-or-equal 1:number, 2:number +] ++mem: storing 1 in location 3 + +:(scenario greater_or_equal_2) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 34 + 3:boolean <- greater-or-equal 1:number, 2:number +] ++mem: storing 1 in location 3 + +:(scenario greater_or_equal_3) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 35 + 3:boolean <- greater-or-equal 1:number, 2:number +] ++mem: storing 0 in location 3 + +:(scenario greater_or_equal_multiple) +recipe main [ + 1:boolean <- greater-or-equal 36, 35, 35 +] ++mem: storing 1 in location 1 + +:(scenario greater_or_equal_multiple_2) +recipe main [ + 1:boolean <- greater-or-equal 36, 35, 36 +] ++mem: storing 0 in location 1 + +:(before "End Primitive Recipe Declarations") +LESSER_OR_EQUAL, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["lesser-or-equal"] = LESSER_OR_EQUAL; +:(before "End Primitive Recipe Implementations") +case LESSER_OR_EQUAL: { + bool result = true; + if (SIZE(ingredients) <= 1) { + raise << current_recipe_name() << ": 'lesser-or-equal' needs at least two ingredients to compare in '" << current_instruction().to_string() << "'\n" << end(); + break; + } + for (long long int i = 0; i < SIZE(ingredients); ++i) { + if (!scalar(ingredients.at(i))) { + raise << current_recipe_name() << ": 'lesser-or-equal' can only compare numbers; got " << current_instruction().ingredients.at(i).original_string << '\n' << end(); + goto finish_lesser_or_equal; + } + } + for (long long int i = /**/1; i < SIZE(ingredients); ++i) { + if (ingredients.at(i-1).at(0) > ingredients.at(i).at(0)) { + result = false; + } + } + finish_lesser_or_equal: + products.resize(1); + products.at(0).push_back(result); + break; +} + +:(scenario lesser_or_equal) +recipe main [ + 1:number <- copy 32 + 2:number <- copy 33 + 3:boolean <- lesser-or-equal 1:number, 2:number +] ++mem: storing 1 in location 3 + +:(scenario lesser_or_equal_2) +recipe main [ + 1:number <- copy 33 + 2:number <- copy 33 + 3:boolean <- lesser-or-equal 1:number, 2:number +] ++mem: storing 1 in location 3 + +:(scenario lesser_or_equal_3) +recipe main [ + 1:number <- copy 34 + 2:number <- copy 33 + 3:boolean <- lesser-or-equal 1:number, 2:number +] ++mem: storing 0 in location 3 + +:(scenario lesser_or_equal_multiple) +recipe main [ + 1:boolean <- lesser-or-equal 34, 35, 35 +] ++mem: storing 1 in location 1 + +:(scenario lesser_or_equal_multiple_2) +recipe main [ + 1:boolean <- lesser-or-equal 34, 35, 34 +] ++mem: storing 0 in location 1 diff --git a/032array.cc b/032array.cc index 7ad84367..1b8d5900 100644 --- a/032array.cc +++ b/032array.cc @@ -332,31 +332,3 @@ case LENGTH: { recipe_ordinal r = current_instruction().operation; if (r == CREATE_ARRAY || r == INDEX || r == INDEX_ADDRESS || r == LENGTH) return false; - -:(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 > >::iterator p = r.properties.begin(); p != r.properties.end(); ++p) { - if (p->first == "lookup") { - r.properties.erase(p); - return; - } - } - assert(false); -} diff --git a/048_check_type_by_instruction.cc b/048_check_type_by_instruction.cc deleted file mode 100644 index dab8916b..00000000 --- a/048_check_type_by_instruction.cc +++ /dev/null @@ -1,59 +0,0 @@ - - -:(after "int main") - Transform.push_back(check_types_by_instruction); - -:(code) -void check_types_by_instruction(const recipe_ordinal r) { - map > 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 diff --git a/048_check_type_by_name.cc b/048_check_type_by_name.cc new file mode 100644 index 00000000..8510d02c --- /dev/null +++ b/048_check_type_by_name.cc @@ -0,0 +1,91 @@ +//: Some simple sanity checks for types, and also attempts to guess them where +//: they aren't provided. +//: +//: You still have to provide the full type the first time you mention a +//: variable in a recipe. You have to explicitly name :offset and :variant +//: every single time. You can't use the same name with multiple types in a +//: single recipe. + +:(scenario transform_warns_on_reusing_name_with_different_type) +% Hide_warnings = true; +recipe main [ + x:number <- copy 1 + x:boolean <- copy 1 +] ++warn: x used with multiple types in main + +:(after "int main") + Transform.push_back(check_types_by_name); + +:(code) +void check_types_by_name(const recipe_ordinal r) { + map > metadata; + for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { + instruction& inst = Recipe[r].steps.at(i); + for (long long int in = 0; in < SIZE(inst.ingredients); ++in) { + deduce_missing_type(metadata, inst.ingredients.at(in)); + check_metadata(metadata, inst.ingredients.at(in), r); + } + for (long long int out = 0; out < SIZE(inst.products); ++out) { + deduce_missing_type(metadata, inst.products.at(out)); + check_metadata(metadata, inst.products.at(out), r); + } + } +} + +void check_metadata(map >& metadata, const reagent& x, const recipe_ordinal r) { + if (is_literal(x)) return; + if (is_raw(x)) return; + // if you use raw locations you're probably doing something unsafe + if (is_integer(x.name)) return; + if (x.types.empty()) return; // will throw a more precise warning elsewhere + if (metadata.find(x.name) == metadata.end()) + metadata[x.name] = x.types; + if (metadata[x.name] != x.types) + raise << x.name << " used with multiple types in " << Recipe[r].name << '\n' << end(); +} + +:(scenario transform_fills_in_missing_types) +recipe main [ + x:number <- copy 1 + y:number <- add x, 1 +] + +:(code) +void deduce_missing_type(map >& metadata, reagent& x) { + if (!x.types.empty()) return; + if (metadata.find(x.name) == metadata.end()) return; + copy(metadata[x.name].begin(), metadata[x.name].end(), inserter(x.types, x.types.begin())); + assert(x.properties.at(0).second.empty()); + x.properties.at(0).second.resize(metadata[x.name].size()); + x.properties.push_back(pair >("as-before", vector())); +} + +:(scenario transform_fills_in_missing_types_in_product) +recipe main [ + x:number <- copy 1 + x <- copy 2 +] + +:(scenario transform_fills_in_missing_types_in_product_and_ingredient) +recipe main [ + x:number <- copy 1 + x <- add x, 1 +] ++mem: storing 2 in location 1 + +:(scenario transform_warns_on_missing_types_in_first_mention) +% Hide_warnings = true; +recipe main [ + x <- copy 1 + x:number <- copy 2 +] ++warn: missing type in 'x <- copy 1' + +:(scenario typo_in_address_type_warns) +% Hide_warnings = true; +recipe main [ + y:address:charcter <- new character:type + *y <- copy 67 +] ++warn: unknown type: charcter diff --git a/049_check_type_by_name.cc b/049_check_type_by_name.cc deleted file mode 100644 index 8510d02c..00000000 --- a/049_check_type_by_name.cc +++ /dev/null @@ -1,91 +0,0 @@ -//: Some simple sanity checks for types, and also attempts to guess them where -//: they aren't provided. -//: -//: You still have to provide the full type the first time you mention a -//: variable in a recipe. You have to explicitly name :offset and :variant -//: every single time. You can't use the same name with multiple types in a -//: single recipe. - -:(scenario transform_warns_on_reusing_name_with_different_type) -% Hide_warnings = true; -recipe main [ - x:number <- copy 1 - x:boolean <- copy 1 -] -+warn: x used with multiple types in main - -:(after "int main") - Transform.push_back(check_types_by_name); - -:(code) -void check_types_by_name(const recipe_ordinal r) { - map > metadata; - for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { - instruction& inst = Recipe[r].steps.at(i); - for (long long int in = 0; in < SIZE(inst.ingredients); ++in) { - deduce_missing_type(metadata, inst.ingredients.at(in)); - check_metadata(metadata, inst.ingredients.at(in), r); - } - for (long long int out = 0; out < SIZE(inst.products); ++out) { - deduce_missing_type(metadata, inst.products.at(out)); - check_metadata(metadata, inst.products.at(out), r); - } - } -} - -void check_metadata(map >& metadata, const reagent& x, const recipe_ordinal r) { - if (is_literal(x)) return; - if (is_raw(x)) return; - // if you use raw locations you're probably doing something unsafe - if (is_integer(x.name)) return; - if (x.types.empty()) return; // will throw a more precise warning elsewhere - if (metadata.find(x.name) == metadata.end()) - metadata[x.name] = x.types; - if (metadata[x.name] != x.types) - raise << x.name << " used with multiple types in " << Recipe[r].name << '\n' << end(); -} - -:(scenario transform_fills_in_missing_types) -recipe main [ - x:number <- copy 1 - y:number <- add x, 1 -] - -:(code) -void deduce_missing_type(map >& metadata, reagent& x) { - if (!x.types.empty()) return; - if (metadata.find(x.name) == metadata.end()) return; - copy(metadata[x.name].begin(), metadata[x.name].end(), inserter(x.types, x.types.begin())); - assert(x.properties.at(0).second.empty()); - x.properties.at(0).second.resize(metadata[x.name].size()); - x.properties.push_back(pair >("as-before", vector())); -} - -:(scenario transform_fills_in_missing_types_in_product) -recipe main [ - x:number <- copy 1 - x <- copy 2 -] - -:(scenario transform_fills_in_missing_types_in_product_and_ingredient) -recipe main [ - x:number <- copy 1 - x <- add x, 1 -] -+mem: storing 2 in location 1 - -:(scenario transform_warns_on_missing_types_in_first_mention) -% Hide_warnings = true; -recipe main [ - x <- copy 1 - x:number <- copy 2 -] -+warn: missing type in 'x <- copy 1' - -:(scenario typo_in_address_type_warns) -% Hide_warnings = true; -recipe main [ - y:address:charcter <- new character:type - *y <- copy 67 -] -+warn: unknown type: charcter -- cgit 1.4.1-2-gfad0