From f59ccc4652bfdb7faf7d77a1685e795342e28fa2 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 13 Jul 2015 14:33:40 -0700 Subject: 1768 --- 041jump_label.cc | 110 +++++++++++++++++++++ 041name.cc | 268 --------------------------------------------------- 042name.cc | 268 +++++++++++++++++++++++++++++++++++++++++++++++++++ 042new.cc | 257 ------------------------------------------------ 043new.cc | 257 ++++++++++++++++++++++++++++++++++++++++++++++++ 043space.cc | 146 ---------------------------- 044space.cc | 146 ++++++++++++++++++++++++++++ 044space_surround.cc | 60 ------------ 045closure_name.cc | 133 ------------------------- 045space_surround.cc | 60 ++++++++++++ 046closure_name.cc | 133 +++++++++++++++++++++++++ 046tangle.cc | 198 ------------------------------------- 047jump_label.cc | 110 --------------------- 048tangle.cc | 198 +++++++++++++++++++++++++++++++++++++ 14 files changed, 1172 insertions(+), 1172 deletions(-) create mode 100644 041jump_label.cc delete mode 100644 041name.cc create mode 100644 042name.cc delete mode 100644 042new.cc create mode 100644 043new.cc delete mode 100644 043space.cc create mode 100644 044space.cc delete mode 100644 044space_surround.cc delete mode 100644 045closure_name.cc create mode 100644 045space_surround.cc create mode 100644 046closure_name.cc delete mode 100644 046tangle.cc delete mode 100644 047jump_label.cc create mode 100644 048tangle.cc diff --git a/041jump_label.cc b/041jump_label.cc new file mode 100644 index 00000000..dca2e37a --- /dev/null +++ b/041jump_label.cc @@ -0,0 +1,110 @@ +//: Support jumps to labels. +//: We'll also treat 'break' and 'continue' as jumps. The choice of name is +//: just documentation about intent. + +:(scenario jump_to_label) +recipe main [ + jump +target:label + 1:number <- copy 0:literal + +target +] +-mem: storing 0 in location 1 + +:(before "End Mu Types Initialization") +Type_ordinal["label"] = 0; + +:(after "int main") + Transform.push_back(transform_labels); + +:(code) +void transform_labels(const recipe_ordinal r) { + map offset; + for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { + const instruction& inst = Recipe[r].steps.at(i); + if (!inst.label.empty()) offset[inst.label] = i; + } + for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { + instruction& inst = Recipe[r].steps.at(i); + if (inst.operation == Recipe_ordinal["jump"]) { +//? cerr << inst.to_string() << '\n'; //? 1 + replace_offset(inst.ingredients.at(0), offset, i, r); + } + if (inst.operation == Recipe_ordinal["jump-if"] || inst.operation == Recipe_ordinal["jump-unless"]) { + replace_offset(inst.ingredients.at(1), offset, i, r); + } + if ((inst.operation == Recipe_ordinal["loop"] || inst.operation == Recipe_ordinal["break"]) + && SIZE(inst.ingredients) == 1) { + replace_offset(inst.ingredients.at(0), offset, i, r); + } + if ((inst.operation == Recipe_ordinal["loop-if"] || inst.operation == Recipe_ordinal["loop-unless"] + || inst.operation == Recipe_ordinal["break-if"] || inst.operation == Recipe_ordinal["break-unless"]) + && SIZE(inst.ingredients) == 2) { + replace_offset(inst.ingredients.at(1), offset, i, r); + } + } +} + +:(code) +void replace_offset(reagent& x, /*const*/ map& offset, const long long int current_offset, const recipe_ordinal r) { +//? cerr << "AAA " << x.to_string() << '\n'; //? 1 + assert(is_literal(x)); +//? cerr << "BBB " << x.to_string() << '\n'; //? 1 + assert(!x.initialized); +//? cerr << "CCC " << x.to_string() << '\n'; //? 1 + if (is_integer(x.name)) return; // non-labels will be handled like other number operands +//? cerr << "DDD " << x.to_string() << '\n'; //? 1 + if (offset.find(x.name) == offset.end()) + raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n'; + x.set_value(offset[x.name]-current_offset); +} + +:(scenario break_to_label) +recipe main [ +#? $print [aaa] + { + { + break +target:label + 1:number <- copy 0:literal + } + } + +target +] +-mem: storing 0 in location 1 + +:(scenario jump_if_to_label) +recipe main [ + { + { + jump-if 1:literal, +target:label + 1:number <- copy 0:literal + } + } + +target +] +-mem: storing 0 in location 1 + +:(scenario loop_unless_to_label) +recipe main [ + { + { + loop-unless 0:literal, +target:label # loop/break with a label don't care about braces + 1:number <- copy 0:literal + } + } + +target +] +-mem: storing 0 in location 1 + +:(scenario jump_runs_code_after_label) +recipe main [ + # first a few lines of padding to exercise the offset computation + 1:number <- copy 0:literal + 2:number <- copy 0:literal + 3:number <- copy 0:literal + jump +target:label + 4:number <- copy 0:literal + +target + 5:number <- copy 0:literal +] ++mem: storing 0 in location 5 +-mem: storing 0 in location 4 diff --git a/041name.cc b/041name.cc deleted file mode 100644 index ccf4a353..00000000 --- a/041name.cc +++ /dev/null @@ -1,268 +0,0 @@ -//: A big convenience high-level languages provide is the ability to name memory -//: locations. In mu, a transform called 'transform_names' provides this -//: convenience. - -:(scenario convert_names) -recipe main [ - x:number <- copy 0:literal -] -+name: assign x 1 -+mem: storing 0 in location 1 - -:(scenario convert_names_warns) -% Hide_warnings = true; -recipe main [ - x:number <- copy y:number -] -+warn: use before set: y in main - -:(after "int main") - Transform.push_back(transform_names); - -:(before "End Globals") -map > Name; -:(after "Clear Other State For recently_added_recipes") -for (long long int i = 0; i < SIZE(recently_added_recipes); ++i) { - Name.erase(recently_added_recipes.at(i)); -} - -:(code) -void transform_names(const recipe_ordinal r) { - bool names_used = false; - bool numeric_locations_used = false; - map& names = Name[r]; - map > metadata; - // store the indices 'used' so far in the map - long long int& curr_idx = names[""]; - ++curr_idx; // avoid using index 0, benign skip in some other cases - for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { - instruction& inst = Recipe[r].steps.at(i); - // Per-recipe Transforms - // map names to addresses - for (long long int in = 0; in < SIZE(inst.ingredients); ++in) { - check_metadata(metadata, inst.ingredients.at(in), r); - if (is_numeric_location(inst.ingredients.at(in))) numeric_locations_used = true; - if (is_named_location(inst.ingredients.at(in))) names_used = true; - if (disqualified(inst.ingredients.at(in))) continue; - if (!already_transformed(inst.ingredients.at(in), names)) { - raise << "use before set: " << inst.ingredients.at(in).name << " in " << Recipe[r].name << '\n'; - } - inst.ingredients.at(in).set_value(lookup_name(inst.ingredients.at(in), r)); - } - for (long long int out = 0; out < SIZE(inst.products); ++out) { - check_metadata(metadata, inst.products.at(out), r); - if (is_numeric_location(inst.products.at(out))) numeric_locations_used = true; - if (is_named_location(inst.products.at(out))) names_used = true; - if (disqualified(inst.products.at(out))) continue; - if (names.find(inst.products.at(out).name) == names.end()) { - trace("name") << "assign " << inst.products.at(out).name << " " << curr_idx; - names[inst.products.at(out).name] = curr_idx; - curr_idx += size_of(inst.products.at(out)); - } - inst.products.at(out).set_value(lookup_name(inst.products.at(out), r)); - } - } - if (names_used && numeric_locations_used && r != Recipe_ordinal["interactive"]) - raise << "mixing variable names and numeric addresses in " << Recipe[r].name << '\n'; -} - -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 (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'; -} - -bool disqualified(/*mutable*/ reagent& x) { -//? cerr << x.to_string() << '\n'; //? 1 - if (x.types.empty()) - raise << "missing type in " << x.to_string() << '\n'; - assert(!x.types.empty()); - if (is_raw(x)) return true; - if (is_literal(x)) return true; - if (is_integer(x.name)) return true; - // End Disqualified Reagents - if (x.initialized) return true; - return false; -} - -bool already_transformed(const reagent& r, const map& names) { - return names.find(r.name) != names.end(); -} - -long long int lookup_name(const reagent& r, const recipe_ordinal default_recipe) { - return Name[default_recipe][r.name]; -} - -type_ordinal skip_addresses(const vector& types) { - for (long long int i = 0; i < SIZE(types); ++i) { - if (types.at(i) != Type_ordinal["address"]) return types.at(i); - } - raise << "expected a container" << '\n' << die(); - return -1; -} - -int find_element_name(const type_ordinal t, const string& name) { - const type_info& container = Type[t]; -//? cout << "looking for element " << name << " in type " << container.name << " with " << SIZE(container.element_names) << " elements\n"; //? 1 - for (long long int i = 0; i < SIZE(container.element_names); ++i) { - if (container.element_names.at(i) == name) return i; - } - raise << "unknown element " << name << " in container " << Type[t].name << '\n' << die(); - return -1; -} - -bool is_numeric_location(const reagent& x) { - if (is_literal(x)) return false; - if (is_raw(x)) return false; - if (x.name == "0") return false; // used for chaining lexical scopes - return is_integer(x.name); -} - -bool is_named_location(const reagent& x) { - if (is_literal(x)) return false; - if (is_raw(x)) return false; - if (is_special_name(x.name)) return false; - return !is_integer(x.name); -} - -bool is_raw(const reagent& r) { - for (long long int i = /*skip value+type*/1; i < SIZE(r.properties); ++i) { - if (r.properties.at(i).first == "raw") return true; - } - return false; -} - -bool is_special_name(const string& s) { - if (s == "_") return true; - if (s == "0") return true; - // End is_special_name Cases - return false; -} - -:(scenario convert_names_passes_dummy) -# _ is just a dummy result that never gets consumed -recipe main [ - _, x:number <- copy 0:literal, 1:literal -] -+name: assign x 1 --name: assign _ 1 - -//: an escape hatch to suppress name conversion that we'll use later -:(scenario convert_names_passes_raw) -recipe main [ - x:number/raw <- copy 0:literal -] --name: assign x 1 - -:(scenario convert_names_warns_when_mixing_names_and_numeric_locations) -% Hide_warnings = true; -recipe main [ - x:number <- copy 1:number -] -+warn: mixing variable names and numeric addresses in main - -:(scenario convert_names_warns_when_mixing_names_and_numeric_locations2) -% Hide_warnings = true; -recipe main [ - x:number <- copy 1:literal - 1:number <- copy x:number -] -+warn: mixing variable names and numeric addresses in main - -:(scenario convert_names_does_not_warn_when_mixing_names_and_raw_locations) -% Hide_warnings = true; -recipe main [ - x:number <- copy 1:number/raw -] --warn: mixing variable names and numeric addresses in main -$warn: 0 - -:(scenario convert_names_does_not_warn_when_mixing_names_and_literals) -% Hide_warnings = true; -recipe main [ - x:number <- copy 1:literal -] --warn: mixing variable names and numeric addresses in main -$warn: 0 - -:(scenario convert_names_warns_on_reusing_name_with_different_type) -% Hide_warnings = true; -recipe main [ - x:number <- copy 1:literal - x:boolean <- copy 1:literal -] -+warn: x used with multiple types in main - -//:: Support element names for containers in 'get' and 'get-address'. - -//: update our running example container for the next test -:(before "End Mu Types Initialization") -Type[point].element_names.push_back("x"); -Type[point].element_names.push_back("y"); -:(scenario convert_names_transforms_container_elements) -recipe main [ - p:address:point <- copy 0:literal # unsafe - a:number <- get p:address:point/deref, y:offset - b:number <- get p:address:point/deref, x:offset -] -+name: element y of type point is at offset 1 -+name: element x of type point is at offset 0 - -:(after "Per-recipe Transforms") -// replace element names of containers with offsets -if (inst.operation == Recipe_ordinal["get"] - || inst.operation == Recipe_ordinal["get-address"]) { - // at least 2 args, and second arg is offset - assert(SIZE(inst.ingredients) >= 2); -//? cout << inst.ingredients.at(1).to_string() << '\n'; //? 1 - if (!is_literal(inst.ingredients.at(1))) - raise << inst.to_string() << ": expected literal; got " << inst.ingredients.at(1).to_string() << '\n' << die(); - if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { - // since first non-address in base type must be a container, we don't have to canonize - type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types); - inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name)); - trace("name") << "element " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " is at offset " << inst.ingredients.at(1).value; - } -} - -//: this test is actually illegal so can't call run -:(scenarios transform) -:(scenario convert_names_handles_containers) -recipe main [ - a:point <- copy 0:literal - b:number <- copy 0:literal -] -+name: assign a 1 -+name: assign b 3 - -//:: Support variant names for exclusive containers in 'maybe-convert'. - -:(scenarios run) -:(scenario maybe_convert_named) -recipe main [ - 12:number <- copy 1:literal - 13:number <- copy 35:literal - 14:number <- copy 36:literal - 20:address:point <- maybe-convert 12:number-or-point/raw, p:variant # unsafe -] -+name: variant p of type number-or-point has tag 1 -+mem: storing 13 in location 20 - -:(after "Per-recipe Transforms") -// convert variant names of exclusive containers -if (inst.operation == Recipe_ordinal["maybe-convert"]) { - // at least 2 args, and second arg is offset - assert(SIZE(inst.ingredients) >= 2); - assert(is_literal(inst.ingredients.at(1))); - if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { - // since first non-address in base type must be an exclusive container, we don't have to canonize - type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types); - inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name)); - trace("name") << "variant " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " has tag " << inst.ingredients.at(1).value; - } -} diff --git a/042name.cc b/042name.cc new file mode 100644 index 00000000..ccf4a353 --- /dev/null +++ b/042name.cc @@ -0,0 +1,268 @@ +//: A big convenience high-level languages provide is the ability to name memory +//: locations. In mu, a transform called 'transform_names' provides this +//: convenience. + +:(scenario convert_names) +recipe main [ + x:number <- copy 0:literal +] ++name: assign x 1 ++mem: storing 0 in location 1 + +:(scenario convert_names_warns) +% Hide_warnings = true; +recipe main [ + x:number <- copy y:number +] ++warn: use before set: y in main + +:(after "int main") + Transform.push_back(transform_names); + +:(before "End Globals") +map > Name; +:(after "Clear Other State For recently_added_recipes") +for (long long int i = 0; i < SIZE(recently_added_recipes); ++i) { + Name.erase(recently_added_recipes.at(i)); +} + +:(code) +void transform_names(const recipe_ordinal r) { + bool names_used = false; + bool numeric_locations_used = false; + map& names = Name[r]; + map > metadata; + // store the indices 'used' so far in the map + long long int& curr_idx = names[""]; + ++curr_idx; // avoid using index 0, benign skip in some other cases + for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { + instruction& inst = Recipe[r].steps.at(i); + // Per-recipe Transforms + // map names to addresses + for (long long int in = 0; in < SIZE(inst.ingredients); ++in) { + check_metadata(metadata, inst.ingredients.at(in), r); + if (is_numeric_location(inst.ingredients.at(in))) numeric_locations_used = true; + if (is_named_location(inst.ingredients.at(in))) names_used = true; + if (disqualified(inst.ingredients.at(in))) continue; + if (!already_transformed(inst.ingredients.at(in), names)) { + raise << "use before set: " << inst.ingredients.at(in).name << " in " << Recipe[r].name << '\n'; + } + inst.ingredients.at(in).set_value(lookup_name(inst.ingredients.at(in), r)); + } + for (long long int out = 0; out < SIZE(inst.products); ++out) { + check_metadata(metadata, inst.products.at(out), r); + if (is_numeric_location(inst.products.at(out))) numeric_locations_used = true; + if (is_named_location(inst.products.at(out))) names_used = true; + if (disqualified(inst.products.at(out))) continue; + if (names.find(inst.products.at(out).name) == names.end()) { + trace("name") << "assign " << inst.products.at(out).name << " " << curr_idx; + names[inst.products.at(out).name] = curr_idx; + curr_idx += size_of(inst.products.at(out)); + } + inst.products.at(out).set_value(lookup_name(inst.products.at(out), r)); + } + } + if (names_used && numeric_locations_used && r != Recipe_ordinal["interactive"]) + raise << "mixing variable names and numeric addresses in " << Recipe[r].name << '\n'; +} + +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 (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'; +} + +bool disqualified(/*mutable*/ reagent& x) { +//? cerr << x.to_string() << '\n'; //? 1 + if (x.types.empty()) + raise << "missing type in " << x.to_string() << '\n'; + assert(!x.types.empty()); + if (is_raw(x)) return true; + if (is_literal(x)) return true; + if (is_integer(x.name)) return true; + // End Disqualified Reagents + if (x.initialized) return true; + return false; +} + +bool already_transformed(const reagent& r, const map& names) { + return names.find(r.name) != names.end(); +} + +long long int lookup_name(const reagent& r, const recipe_ordinal default_recipe) { + return Name[default_recipe][r.name]; +} + +type_ordinal skip_addresses(const vector& types) { + for (long long int i = 0; i < SIZE(types); ++i) { + if (types.at(i) != Type_ordinal["address"]) return types.at(i); + } + raise << "expected a container" << '\n' << die(); + return -1; +} + +int find_element_name(const type_ordinal t, const string& name) { + const type_info& container = Type[t]; +//? cout << "looking for element " << name << " in type " << container.name << " with " << SIZE(container.element_names) << " elements\n"; //? 1 + for (long long int i = 0; i < SIZE(container.element_names); ++i) { + if (container.element_names.at(i) == name) return i; + } + raise << "unknown element " << name << " in container " << Type[t].name << '\n' << die(); + return -1; +} + +bool is_numeric_location(const reagent& x) { + if (is_literal(x)) return false; + if (is_raw(x)) return false; + if (x.name == "0") return false; // used for chaining lexical scopes + return is_integer(x.name); +} + +bool is_named_location(const reagent& x) { + if (is_literal(x)) return false; + if (is_raw(x)) return false; + if (is_special_name(x.name)) return false; + return !is_integer(x.name); +} + +bool is_raw(const reagent& r) { + for (long long int i = /*skip value+type*/1; i < SIZE(r.properties); ++i) { + if (r.properties.at(i).first == "raw") return true; + } + return false; +} + +bool is_special_name(const string& s) { + if (s == "_") return true; + if (s == "0") return true; + // End is_special_name Cases + return false; +} + +:(scenario convert_names_passes_dummy) +# _ is just a dummy result that never gets consumed +recipe main [ + _, x:number <- copy 0:literal, 1:literal +] ++name: assign x 1 +-name: assign _ 1 + +//: an escape hatch to suppress name conversion that we'll use later +:(scenario convert_names_passes_raw) +recipe main [ + x:number/raw <- copy 0:literal +] +-name: assign x 1 + +:(scenario convert_names_warns_when_mixing_names_and_numeric_locations) +% Hide_warnings = true; +recipe main [ + x:number <- copy 1:number +] ++warn: mixing variable names and numeric addresses in main + +:(scenario convert_names_warns_when_mixing_names_and_numeric_locations2) +% Hide_warnings = true; +recipe main [ + x:number <- copy 1:literal + 1:number <- copy x:number +] ++warn: mixing variable names and numeric addresses in main + +:(scenario convert_names_does_not_warn_when_mixing_names_and_raw_locations) +% Hide_warnings = true; +recipe main [ + x:number <- copy 1:number/raw +] +-warn: mixing variable names and numeric addresses in main +$warn: 0 + +:(scenario convert_names_does_not_warn_when_mixing_names_and_literals) +% Hide_warnings = true; +recipe main [ + x:number <- copy 1:literal +] +-warn: mixing variable names and numeric addresses in main +$warn: 0 + +:(scenario convert_names_warns_on_reusing_name_with_different_type) +% Hide_warnings = true; +recipe main [ + x:number <- copy 1:literal + x:boolean <- copy 1:literal +] ++warn: x used with multiple types in main + +//:: Support element names for containers in 'get' and 'get-address'. + +//: update our running example container for the next test +:(before "End Mu Types Initialization") +Type[point].element_names.push_back("x"); +Type[point].element_names.push_back("y"); +:(scenario convert_names_transforms_container_elements) +recipe main [ + p:address:point <- copy 0:literal # unsafe + a:number <- get p:address:point/deref, y:offset + b:number <- get p:address:point/deref, x:offset +] ++name: element y of type point is at offset 1 ++name: element x of type point is at offset 0 + +:(after "Per-recipe Transforms") +// replace element names of containers with offsets +if (inst.operation == Recipe_ordinal["get"] + || inst.operation == Recipe_ordinal["get-address"]) { + // at least 2 args, and second arg is offset + assert(SIZE(inst.ingredients) >= 2); +//? cout << inst.ingredients.at(1).to_string() << '\n'; //? 1 + if (!is_literal(inst.ingredients.at(1))) + raise << inst.to_string() << ": expected literal; got " << inst.ingredients.at(1).to_string() << '\n' << die(); + if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { + // since first non-address in base type must be a container, we don't have to canonize + type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types); + inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name)); + trace("name") << "element " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " is at offset " << inst.ingredients.at(1).value; + } +} + +//: this test is actually illegal so can't call run +:(scenarios transform) +:(scenario convert_names_handles_containers) +recipe main [ + a:point <- copy 0:literal + b:number <- copy 0:literal +] ++name: assign a 1 ++name: assign b 3 + +//:: Support variant names for exclusive containers in 'maybe-convert'. + +:(scenarios run) +:(scenario maybe_convert_named) +recipe main [ + 12:number <- copy 1:literal + 13:number <- copy 35:literal + 14:number <- copy 36:literal + 20:address:point <- maybe-convert 12:number-or-point/raw, p:variant # unsafe +] ++name: variant p of type number-or-point has tag 1 ++mem: storing 13 in location 20 + +:(after "Per-recipe Transforms") +// convert variant names of exclusive containers +if (inst.operation == Recipe_ordinal["maybe-convert"]) { + // at least 2 args, and second arg is offset + assert(SIZE(inst.ingredients) >= 2); + assert(is_literal(inst.ingredients.at(1))); + if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { + // since first non-address in base type must be an exclusive container, we don't have to canonize + type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types); + inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name)); + trace("name") << "variant " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " has tag " << inst.ingredients.at(1).value; + } +} diff --git a/042new.cc b/042new.cc deleted file mode 100644 index 5600f443..00000000 --- a/042new.cc +++ /dev/null @@ -1,257 +0,0 @@ -//: A simple memory allocator to create space for new variables at runtime. - -:(scenarios run) -:(scenario new) -# call new two times with identical arguments; you should get back different results -recipe main [ - 1:address:number/raw <- new number:type - 2:address:number/raw <- new number:type - 3:boolean/raw <- equal 1:address:number/raw, 2:address:number/raw -] -+mem: storing 0 in location 3 - -:(before "End Globals") -long long int Reserved_for_tests = 1000; -long long int Memory_allocated_until = Reserved_for_tests; -long long int Initial_memory_per_routine = 100000; -:(before "End Setup") -Memory_allocated_until = Reserved_for_tests; -Initial_memory_per_routine = 100000; -:(before "End routine Fields") -long long int alloc, alloc_max; -:(before "End routine Constructor") -alloc = Memory_allocated_until; -Memory_allocated_until += Initial_memory_per_routine; -alloc_max = Memory_allocated_until; -trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << alloc << " to " << alloc_max; - -//:: First handle 'type' operands. - -:(before "End Mu Types Initialization") -Type_ordinal["type"] = 0; -:(after "Per-recipe Transforms") -// replace type names with type_ordinals -if (inst.operation == Recipe_ordinal["new"]) { - // End NEW Transform Special-cases - // first arg must be of type 'type' - assert(SIZE(inst.ingredients) >= 1); - if (!is_literal(inst.ingredients.at(0))) - raise << "expected literal, got " << inst.ingredients.at(0).to_string() << '\n' << die(); - if (inst.ingredients.at(0).properties.at(0).second.at(0) != "type") - raise << "tried to allocate non-type " << inst.ingredients.at(0).to_string() << " in recipe " << Recipe[r].name << '\n' << die(); - if (Type_ordinal.find(inst.ingredients.at(0).name) == Type_ordinal.end()) - raise << "unknown type " << inst.ingredients.at(0).name << " in recipe " << Recipe[r].name << '\n' << die(); -//? cerr << "type " << inst.ingredients.at(0).name << " => " << Type_ordinal[inst.ingredients.at(0).name] << '\n'; //? 1 - inst.ingredients.at(0).set_value(Type_ordinal[inst.ingredients.at(0).name]); - trace(Primitive_recipe_depth, "new") << inst.ingredients.at(0).name << " -> " << inst.ingredients.at(0).name; - end_new_transform:; -} - -//:: Now implement the primitive recipe. -//: todo: build 'new' in mu itself - -:(before "End Primitive Recipe Declarations") -NEW, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["new"] = NEW; -:(before "End Primitive Recipe Implementations") -case NEW: { - // compute the space we need - long long int size = 0; - long long int array_length = 0; - { - vector type; - assert(is_literal(current_instruction().ingredients.at(0))); - type.push_back(current_instruction().ingredients.at(0).value); -//? trace(Primitive_recipe_depth, "mem") << "type " << current_instruction().ingredients.at(0).to_string() << ' ' << type.size() << ' ' << type.back() << " has size " << size_of(type); //? 1 - if (SIZE(current_instruction().ingredients) > 1) { - // array - array_length = ingredients.at(1).at(0); - trace(Primitive_recipe_depth, "mem") << "array size is " << array_length; - size = array_length*size_of(type) + /*space for length*/1; - } - else { - // scalar - size = size_of(type); - } - } - // compute the region of memory to return - // really crappy at the moment - ensure_space(size); - const long long int result = Current_routine->alloc; - trace(Primitive_recipe_depth, "mem") << "new alloc: " << result; -//? trace(Primitive_recipe_depth, "mem") << "size: " << size << " locations"; //? 1 - // save result - products.resize(1); - products.at(0).push_back(result); - // initialize allocated space - for (long long int address = result; address < result+size; ++address) { - Memory[address] = 0; - } - if (SIZE(current_instruction().ingredients) > 1) { - Memory[result] = array_length; - } - // bump - Current_routine->alloc += size; - // no support for reclaiming memory - assert(Current_routine->alloc <= Current_routine->alloc_max); - break; -} - -:(code) -void ensure_space(long long int size) { - assert(size <= Initial_memory_per_routine); -//? cout << Current_routine->alloc << " " << Current_routine->alloc_max << " " << size << '\n'; //? 1 - if (Current_routine->alloc + size > Current_routine->alloc_max) { - // waste the remaining space and create a new chunk - Current_routine->alloc = Memory_allocated_until; - Memory_allocated_until += Initial_memory_per_routine; - Current_routine->alloc_max = Memory_allocated_until; - trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max; - } -} - -:(scenario new_initializes) -% Memory_allocated_until = 10; -% Memory[Memory_allocated_until] = 1; -recipe main [ - 1:address:number <- new number:type - 2:number <- copy 1:address:number/deref -] -+mem: storing 0 in location 2 - -:(scenario new_array) -recipe main [ - 1:address:array:number/raw <- new number:type, 5:literal - 2:address:number/raw <- new number:type - 3:number/raw <- subtract 2:address:number/raw, 1:address:array:number/raw -] -+run: 1:address:array:number/raw <- new number:type, 5:literal -+mem: array size is 5 -# don't forget the extra location for array size -+mem: storing 6 in location 3 - -:(scenario new_empty_array) -recipe main [ - 1:address:array:number/raw <- new number:type, 0:literal - 2:address:number/raw <- new number:type - 3:number/raw <- subtract 2:address:number/raw, 1:address:array:number/raw -] -+run: 1:address:array:number/raw <- new number:type, 0:literal -+mem: array size is 0 -+mem: storing 1 in location 3 - -//: Make sure that each routine gets a different alloc to start. -:(scenario new_concurrent) -recipe f1 [ - start-running f2:recipe - 1:address:number/raw <- new number:type - # wait for f2 to complete - { - loop-unless 4:number/raw - } -] -recipe f2 [ - 2:address:number/raw <- new number:type - # hack: assumes scheduler implementation - 3:boolean/raw <- equal 1:address:number/raw, 2:address:number/raw - # signal f2 complete - 4:number/raw <- copy 1:literal -] -+mem: storing 0 in location 3 - -//: If a routine runs out of its initial allocation, it should allocate more. -:(scenario new_overflow) -% Initial_memory_per_routine = 2; -recipe main [ - 1:address:number/raw <- new number:type - 2:address:point/raw <- new point:type # not enough room in initial page -] -+new: routine allocated memory from 1000 to 1002 -+new: routine allocated memory from 1002 to 1004 - -//:: Next, extend 'new' to handle a unicode string literal argument. - -:(scenario new_string) -recipe main [ - 1:address:array:character <- new [abc def] - 2:character <- index 1:address:array:character/deref, 5:literal -] -# number code for 'e' -+mem: storing 101 in location 2 - -:(scenario new_string_handles_unicode) -recipe main [ - 1:address:array:character <- new [a«c] - 2:number <- length 1:address:array:character/deref - 3:character <- index 1:address:array:character/deref, 1:literal -] -+mem: storing 3 in location 2 -# unicode for '«' -+mem: storing 171 in location 3 - -:(before "End NEW Transform Special-cases") - if (!inst.ingredients.empty() - && !inst.ingredients.at(0).properties.empty() - && !inst.ingredients.at(0).properties.at(0).second.empty() - && inst.ingredients.at(0).properties.at(0).second.at(0) == "literal-string") { - // skip transform - inst.ingredients.at(0).initialized = true; - goto end_new_transform; - } - -:(after "case NEW" following "Primitive Recipe Implementations") -if (is_literal(current_instruction().ingredients.at(0)) - && current_instruction().ingredients.at(0).properties.at(0).second.at(0) == "literal-string") { - products.resize(1); - products.at(0).push_back(new_string(current_instruction().ingredients.at(0).name)); - break; -} - -:(code) -long long int new_string(const string& contents) { - // allocate an array just large enough for it - long long int string_length = unicode_length(contents); -//? cout << "string_length is " << string_length << '\n'; //? 1 - ensure_space(string_length+1); // don't forget the extra location for array size - // initialize string -//? cout << "new string literal: " << current_instruction().ingredients.at(0).name << '\n'; //? 1 - long long int result = Current_routine->alloc; - Memory[Current_routine->alloc++] = string_length; - long long int curr = 0; - const char* raw_contents = contents.c_str(); - for (long long int i = 0; i < string_length; ++i) { - uint32_t curr_character; - assert(curr < SIZE(contents)); - tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]); - Memory[Current_routine->alloc] = curr_character; - curr += tb_utf8_char_length(raw_contents[curr]); - ++Current_routine->alloc; - } - // mu strings are not null-terminated in memory - return result; -} - -//: Allocate more to routine when initializing a literal string -:(scenario new_string_overflow) -% Initial_memory_per_routine = 2; -recipe main [ - 1:address:number/raw <- new number:type - 2:address:array:character/raw <- new [a] # not enough room in initial page, if you take the array size into account -] -+new: routine allocated memory from 1000 to 1002 -+new: routine allocated memory from 1002 to 1004 - -//: helpers -:(code) -long long int unicode_length(const string& s) { - const char* in = s.c_str(); - long long int result = 0; - long long int curr = 0; - while (curr < SIZE(s)) { // carefully bounds-check on the string - // before accessing its raw pointer - ++result; - curr += tb_utf8_char_length(in[curr]); - } - return result; -} diff --git a/043new.cc b/043new.cc new file mode 100644 index 00000000..5600f443 --- /dev/null +++ b/043new.cc @@ -0,0 +1,257 @@ +//: A simple memory allocator to create space for new variables at runtime. + +:(scenarios run) +:(scenario new) +# call new two times with identical arguments; you should get back different results +recipe main [ + 1:address:number/raw <- new number:type + 2:address:number/raw <- new number:type + 3:boolean/raw <- equal 1:address:number/raw, 2:address:number/raw +] ++mem: storing 0 in location 3 + +:(before "End Globals") +long long int Reserved_for_tests = 1000; +long long int Memory_allocated_until = Reserved_for_tests; +long long int Initial_memory_per_routine = 100000; +:(before "End Setup") +Memory_allocated_until = Reserved_for_tests; +Initial_memory_per_routine = 100000; +:(before "End routine Fields") +long long int alloc, alloc_max; +:(before "End routine Constructor") +alloc = Memory_allocated_until; +Memory_allocated_until += Initial_memory_per_routine; +alloc_max = Memory_allocated_until; +trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << alloc << " to " << alloc_max; + +//:: First handle 'type' operands. + +:(before "End Mu Types Initialization") +Type_ordinal["type"] = 0; +:(after "Per-recipe Transforms") +// replace type names with type_ordinals +if (inst.operation == Recipe_ordinal["new"]) { + // End NEW Transform Special-cases + // first arg must be of type 'type' + assert(SIZE(inst.ingredients) >= 1); + if (!is_literal(inst.ingredients.at(0))) + raise << "expected literal, got " << inst.ingredients.at(0).to_string() << '\n' << die(); + if (inst.ingredients.at(0).properties.at(0).second.at(0) != "type") + raise << "tried to allocate non-type " << inst.ingredients.at(0).to_string() << " in recipe " << Recipe[r].name << '\n' << die(); + if (Type_ordinal.find(inst.ingredients.at(0).name) == Type_ordinal.end()) + raise << "unknown type " << inst.ingredients.at(0).name << " in recipe " << Recipe[r].name << '\n' << die(); +//? cerr << "type " << inst.ingredients.at(0).name << " => " << Type_ordinal[inst.ingredients.at(0).name] << '\n'; //? 1 + inst.ingredients.at(0).set_value(Type_ordinal[inst.ingredients.at(0).name]); + trace(Primitive_recipe_depth, "new") << inst.ingredients.at(0).name << " -> " << inst.ingredients.at(0).name; + end_new_transform:; +} + +//:: Now implement the primitive recipe. +//: todo: build 'new' in mu itself + +:(before "End Primitive Recipe Declarations") +NEW, +:(before "End Primitive Recipe Numbers") +Recipe_ordinal["new"] = NEW; +:(before "End Primitive Recipe Implementations") +case NEW: { + // compute the space we need + long long int size = 0; + long long int array_length = 0; + { + vector type; + assert(is_literal(current_instruction().ingredients.at(0))); + type.push_back(current_instruction().ingredients.at(0).value); +//? trace(Primitive_recipe_depth, "mem") << "type " << current_instruction().ingredients.at(0).to_string() << ' ' << type.size() << ' ' << type.back() << " has size " << size_of(type); //? 1 + if (SIZE(current_instruction().ingredients) > 1) { + // array + array_length = ingredients.at(1).at(0); + trace(Primitive_recipe_depth, "mem") << "array size is " << array_length; + size = array_length*size_of(type) + /*space for length*/1; + } + else { + // scalar + size = size_of(type); + } + } + // compute the region of memory to return + // really crappy at the moment + ensure_space(size); + const long long int result = Current_routine->alloc; + trace(Primitive_recipe_depth, "mem") << "new alloc: " << result; +//? trace(Primitive_recipe_depth, "mem") << "size: " << size << " locations"; //? 1 + // save result + products.resize(1); + products.at(0).push_back(result); + // initialize allocated space + for (long long int address = result; address < result+size; ++address) { + Memory[address] = 0; + } + if (SIZE(current_instruction().ingredients) > 1) { + Memory[result] = array_length; + } + // bump + Current_routine->alloc += size; + // no support for reclaiming memory + assert(Current_routine->alloc <= Current_routine->alloc_max); + break; +} + +:(code) +void ensure_space(long long int size) { + assert(size <= Initial_memory_per_routine); +//? cout << Current_routine->alloc << " " << Current_routine->alloc_max << " " << size << '\n'; //? 1 + if (Current_routine->alloc + size > Current_routine->alloc_max) { + // waste the remaining space and create a new chunk + Current_routine->alloc = Memory_allocated_until; + Memory_allocated_until += Initial_memory_per_routine; + Current_routine->alloc_max = Memory_allocated_until; + trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max; + } +} + +:(scenario new_initializes) +% Memory_allocated_until = 10; +% Memory[Memory_allocated_until] = 1; +recipe main [ + 1:address:number <- new number:type + 2:number <- copy 1:address:number/deref +] ++mem: storing 0 in location 2 + +:(scenario new_array) +recipe main [ + 1:address:array:number/raw <- new number:type, 5:literal + 2:address:number/raw <- new number:type + 3:number/raw <- subtract 2:address:number/raw, 1:address:array:number/raw +] ++run: 1:address:array:number/raw <- new number:type, 5:literal ++mem: array size is 5 +# don't forget the extra location for array size ++mem: storing 6 in location 3 + +:(scenario new_empty_array) +recipe main [ + 1:address:array:number/raw <- new number:type, 0:literal + 2:address:number/raw <- new number:type + 3:number/raw <- subtract 2:address:number/raw, 1:address:array:number/raw +] ++run: 1:address:array:number/raw <- new number:type, 0:literal ++mem: array size is 0 ++mem: storing 1 in location 3 + +//: Make sure that each routine gets a different alloc to start. +:(scenario new_concurrent) +recipe f1 [ + start-running f2:recipe + 1:address:number/raw <- new number:type + # wait for f2 to complete + { + loop-unless 4:number/raw + } +] +recipe f2 [ + 2:address:number/raw <- new number:type + # hack: assumes scheduler implementation + 3:boolean/raw <- equal 1:address:number/raw, 2:address:number/raw + # signal f2 complete + 4:number/raw <- copy 1:literal +] ++mem: storing 0 in location 3 + +//: If a routine runs out of its initial allocation, it should allocate more. +:(scenario new_overflow) +% Initial_memory_per_routine = 2; +recipe main [ + 1:address:number/raw <- new number:type + 2:address:point/raw <- new point:type # not enough room in initial page +] ++new: routine allocated memory from 1000 to 1002 ++new: routine allocated memory from 1002 to 1004 + +//:: Next, extend 'new' to handle a unicode string literal argument. + +:(scenario new_string) +recipe main [ + 1:address:array:character <- new [abc def] + 2:character <- index 1:address:array:character/deref, 5:literal +] +# number code for 'e' ++mem: storing 101 in location 2 + +:(scenario new_string_handles_unicode) +recipe main [ + 1:address:array:character <- new [a«c] + 2:number <- length 1:address:array:character/deref + 3:character <- index 1:address:array:character/deref, 1:literal +] ++mem: storing 3 in location 2 +# unicode for '«' ++mem: storing 171 in location 3 + +:(before "End NEW Transform Special-cases") + if (!inst.ingredients.empty() + && !inst.ingredients.at(0).properties.empty() + && !inst.ingredients.at(0).properties.at(0).second.empty() + && inst.ingredients.at(0).properties.at(0).second.at(0) == "literal-string") { + // skip transform + inst.ingredients.at(0).initialized = true; + goto end_new_transform; + } + +:(after "case NEW" following "Primitive Recipe Implementations") +if (is_literal(current_instruction().ingredients.at(0)) + && current_instruction().ingredients.at(0).properties.at(0).second.at(0) == "literal-string") { + products.resize(1); + products.at(0).push_back(new_string(current_instruction().ingredients.at(0).name)); + break; +} + +:(code) +long long int new_string(const string& contents) { + // allocate an array just large enough for it + long long int string_length = unicode_length(contents); +//? cout << "string_length is " << string_length << '\n'; //? 1 + ensure_space(string_length+1); // don't forget the extra location for array size + // initialize string +//? cout << "new string literal: " << current_instruction().ingredients.at(0).name << '\n'; //? 1 + long long int result = Current_routine->alloc; + Memory[Current_routine->alloc++] = string_length; + long long int curr = 0; + const char* raw_contents = contents.c_str(); + for (long long int i = 0; i < string_length; ++i) { + uint32_t curr_character; + assert(curr < SIZE(contents)); + tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]); + Memory[Current_routine->alloc] = curr_character; + curr += tb_utf8_char_length(raw_contents[curr]); + ++Current_routine->alloc; + } + // mu strings are not null-terminated in memory + return result; +} + +//: Allocate more to routine when initializing a literal string +:(scenario new_string_overflow) +% Initial_memory_per_routine = 2; +recipe main [ + 1:address:number/raw <- new number:type + 2:address:array:character/raw <- new [a] # not enough room in initial page, if you take the array size into account +] ++new: routine allocated memory from 1000 to 1002 ++new: routine allocated memory from 1002 to 1004 + +//: helpers +:(code) +long long int unicode_length(const string& s) { + const char* in = s.c_str(); + long long int result = 0; + long long int curr = 0; + while (curr < SIZE(s)) { // carefully bounds-check on the string + // before accessing its raw pointer + ++result; + curr += tb_utf8_char_length(in[curr]); + } + return result; +} diff --git a/043space.cc b/043space.cc deleted file mode 100644 index e32fbceb..00000000 --- a/043space.cc +++ /dev/null @@ -1,146 +0,0 @@ -//: Spaces help isolate recipes from each other. You can create them at will, -//: and all addresses in arguments are implicitly based on the 'default-space' -//: (unless they have the /raw property) - -:(scenario set_default_space) -# if default-space is 10, and if an array of 5 locals lies from location 11 to 15 (inclusive), -# then location 0 is really location 11, location 1 is really location 12, and so on. -recipe main [ - 10:number <- copy 5:literal # pretend array; in practice we'll use new - default-space:address:array:location <- copy 10:literal - 1:number <- copy 23:literal -] -+mem: storing 23 in location 12 - -:(scenario deref_sidesteps_default_space) -recipe main [ - # pretend pointer from outside - 3:number <- copy 34:literal - # pretend array - 1000:number <- copy 5:literal - # actual start of this recipe - default-space:address:array:location <- copy 1000:literal - 1:address:number <- copy 3:literal - 8:number/raw <- copy 1:address:number/deref -] -+mem: storing 34 in location 8 - -//:: first disable name conversion for 'default-space' -:(scenario convert_names_passes_default_space) -recipe main [ - default-space:number, x:number <- copy 0:literal, 1:literal -] -+name: assign x 1 --name: assign default-space 1 - -:(before "End Disqualified Reagents") -if (x.name == "default-space") - x.initialized = true; -:(before "End is_special_name Cases") -if (s == "default-space") return true; - -//:: now implement space support -:(before "End call Fields") -long long int default_space; -:(before "End call Constructor") -default_space = 0; - -:(replace "reagent r = x" following "reagent canonize(reagent x)") -reagent r = absolutize(x); -:(code) -reagent absolutize(reagent x) { -//? if (Recipe_ordinal.find("increment-counter") != Recipe_ordinal.end()) //? 1 -//? cout << "AAA " << "increment-counter/2: " << Recipe[Recipe_ordinal["increment-counter"]].steps.at(2).products.at(0).to_string() << '\n'; //? 1 -//? cout << "absolutize " << x.to_string() << '\n'; //? 4 -//? cout << is_raw(x) << '\n'; //? 1 - if (is_raw(x) || is_dummy(x)) return x; -//? cout << "not raw: " << x.to_string() << '\n'; //? 1 - if (!x.initialized) - raise << current_instruction().to_string() << ": reagent not initialized: " << x.to_string() << die(); - reagent r = x; - r.set_value(address(r.value, space_base(r))); -//? cout << "after absolutize: " << r.value << '\n'; //? 1 - r.properties.push_back(pair >("raw", vector())); - assert(is_raw(r)); - return r; -} -:(before "return result" following "reagent deref(reagent x)") -result.properties.push_back(pair >("raw", vector())); - -//:: fix 'get' - -:(scenario deref_sidesteps_default_space_in_get) -recipe main [ - # pretend pointer to container from outside - 12:number <- copy 34:literal - 13:number <- copy 35:literal - # pretend array - 1000:number <- copy 5:literal - # actual start of this recipe - default-space:address:array:location <- copy 1000:literal - 1:address:point <- copy 12:literal - 9:number/raw <- get 1:address:point/deref, 1:offset -] -+mem: storing 35 in location 9 - -:(after "reagent tmp" following "case GET:") -tmp.properties.push_back(pair >("raw", vector())); - -//:: fix 'index' - -:(scenario deref_sidesteps_default_space_in_index) -recipe main [ - # pretend pointer to array from outside - 12:number <- copy 2:literal - 13:number <- copy 34:literal - 14:number <- copy 35:literal - # pretend array - 1000:number <- copy 5:literal - # actual start of this recipe - default-space:address:array:location <- copy 1000:literal - 1:address:array:number <- copy 12:literal - 9:number/raw <- index 1:address:array:number/deref, 1:literal -] -+mem: storing 35 in location 9 - -:(after "reagent tmp" following "case INDEX:") -tmp.properties.push_back(pair >("raw", vector())); - -//:: helpers - -:(code) -long long int space_base(const reagent& x) { - return Current_routine->calls.front().default_space; -} - -long long int address(long long int offset, long long int base) { - if (base == 0) return offset; // raw -//? cout << base << '\n'; //? 2 - if (offset >= static_cast(Memory[base])) { - // todo: test - raise << "location " << offset << " is out of bounds " << Memory[base] << " at " << base << '\n'; - } - return base+1 + offset; -} - -:(after "void write_memory(reagent x, vector data)") - if (x.name == "default-space") { - assert(scalar(data)); - Current_routine->calls.front().default_space = data.at(0); -//? cout << "AAA " << Current_routine->calls.front().default_space << '\n'; //? 1 - return; - } - -:(scenario get_default_space) -recipe main [ - default-space:address:array:location <- copy 10:literal - 1:number/raw <- copy default-space:address:array:location -] -+mem: storing 10 in location 1 - -:(after "vector read_memory(reagent x)") - if (x.name == "default-space") { - vector result; - result.push_back(Current_routine->calls.front().default_space); - return result; - } diff --git a/044space.cc b/044space.cc new file mode 100644 index 00000000..e32fbceb --- /dev/null +++ b/044space.cc @@ -0,0 +1,146 @@ +//: Spaces help isolate recipes from each other. You can create them at will, +//: and all addresses in arguments are implicitly based on the 'default-space' +//: (unless they have the /raw property) + +:(scenario set_default_space) +# if default-space is 10, and if an array of 5 locals lies from location 11 to 15 (inclusive), +# then location 0 is really location 11, location 1 is really location 12, and so on. +recipe main [ + 10:number <- copy 5:literal # pretend array; in practice we'll use new + default-space:address:array:location <- copy 10:literal + 1:number <- copy 23:literal +] ++mem: storing 23 in location 12 + +:(scenario deref_sidesteps_default_space) +recipe main [ + # pretend pointer from outside + 3:number <- copy 34:literal + # pretend array + 1000:number <- copy 5:literal + # actual start of this recipe + default-space:address:array:location <- copy 1000:literal + 1:address:number <- copy 3:literal + 8:number/raw <- copy 1:address:number/deref +] ++mem: storing 34 in location 8 + +//:: first disable name conversion for 'default-space' +:(scenario convert_names_passes_default_space) +recipe main [ + default-space:number, x:number <- copy 0:literal, 1:literal +] ++name: assign x 1 +-name: assign default-space 1 + +:(before "End Disqualified Reagents") +if (x.name == "default-space") + x.initialized = true; +:(before "End is_special_name Cases") +if (s == "default-space") return true; + +//:: now implement space support +:(before "End call Fields") +long long int default_space; +:(before "End call Constructor") +default_space = 0; + +:(replace "reagent r = x" following "reagent canonize(reagent x)") +reagent r = absolutize(x); +:(code) +reagent absolutize(reagent x) { +//? if (Recipe_ordinal.find("increment-counter") != Recipe_ordinal.end()) //? 1 +//? cout << "AAA " << "increment-counter/2: " << Recipe[Recipe_ordinal["increment-counter"]].steps.at(2).products.at(0).to_string() << '\n'; //? 1 +//? cout << "absolutize " << x.to_string() << '\n'; //? 4 +//? cout << is_raw(x) << '\n'; //? 1 + if (is_raw(x) || is_dummy(x)) return x; +//? cout << "not raw: " << x.to_string() << '\n'; //? 1 + if (!x.initialized) + raise << current_instruction().to_string() << ": reagent not initialized: " << x.to_string() << die(); + reagent r = x; + r.set_value(address(r.value, space_base(r))); +//? cout << "after absolutize: " << r.value << '\n'; //? 1 + r.properties.push_back(pair >("raw", vector())); + assert(is_raw(r)); + return r; +} +:(before "return result" following "reagent deref(reagent x)") +result.properties.push_back(pair >("raw", vector())); + +//:: fix 'get' + +:(scenario deref_sidesteps_default_space_in_get) +recipe main [ + # pretend pointer to container from outside + 12:number <- copy 34:literal + 13:number <- copy 35:literal + # pretend array + 1000:number <- copy 5:literal + # actual start of this recipe + default-space:address:array:location <- copy 1000:literal + 1:address:point <- copy 12:literal + 9:number/raw <- get 1:address:point/deref, 1:offset +] ++mem: storing 35 in location 9 + +:(after "reagent tmp" following "case GET:") +tmp.properties.push_back(pair >("raw", vector())); + +//:: fix 'index' + +:(scenario deref_sidesteps_default_space_in_index) +recipe main [ + # pretend pointer to array from outside + 12:number <- copy 2:literal + 13:number <- copy 34:literal + 14:number <- copy 35:literal + # pretend array + 1000:number <- copy 5:literal + # actual start of this recipe + default-space:address:array:location <- copy 1000:literal + 1:address:array:number <- copy 12:literal + 9:number/raw <- index 1:address:array:number/deref, 1:literal +] ++mem: storing 35 in location 9 + +:(after "reagent tmp" following "case INDEX:") +tmp.properties.push_back(pair >("raw", vector())); + +//:: helpers + +:(code) +long long int space_base(const reagent& x) { + return Current_routine->calls.front().default_space; +} + +long long int address(long long int offset, long long int base) { + if (base == 0) return offset; // raw +//? cout << base << '\n'; //? 2 + if (offset >= static_cast(Memory[base])) { + // todo: test + raise << "location " << offset << " is out of bounds " << Memory[base] << " at " << base << '\n'; + } + return base+1 + offset; +} + +:(after "void write_memory(reagent x, vector data)") + if (x.name == "default-space") { + assert(scalar(data)); + Current_routine->calls.front().default_space = data.at(0); +//? cout << "AAA " << Current_routine->calls.front().default_space << '\n'; //? 1 + return; + } + +:(scenario get_default_space) +recipe main [ + default-space:address:array:location <- copy 10:literal + 1:number/raw <- copy default-space:address:array:location +] ++mem: storing 10 in location 1 + +:(after "vector read_memory(reagent x)") + if (x.name == "default-space") { + vector result; + result.push_back(Current_routine->calls.front().default_space); + return result; + } diff --git a/044space_surround.cc b/044space_surround.cc deleted file mode 100644 index 61521013..00000000 --- a/044space_surround.cc +++ /dev/null @@ -1,60 +0,0 @@ -//: So far you can have global variables by not setting default-space, and -//: local variables by setting default-space. You can isolate variables -//: between those extremes by creating 'surrounding' spaces. -//: -//: (Surrounding spaces are like lexical scopes in other languages.) - -:(scenario surrounding_space) -# location 1 in space 1 refers to the space surrounding the default space, here 20. -recipe main [ - 10:number <- copy 5:literal # pretend array - 20:number <- copy 5:literal # pretend array - default-space:address:array:location <- copy 10:literal - 0:address:array:location/names:dummy <- copy 20:literal # later layers will explain the /names: property - 1:number <- copy 32:literal - 1:number/space:1 <- copy 33:literal -] -# chain space -+mem: storing 20 in location 11 -# store to default-space -+mem: storing 32 in location 12 -# store to chained space -+mem: storing 33 in location 22 - -//: If you think of a space as a collection of variables with a common -//: lifetime, surrounding allows managing shorter lifetimes inside a longer -//: one. - -:(replace{} "long long int space_base(const reagent& x)") -long long int space_base(const reagent& x) { -//? cerr << "space_base: " << x.to_string() << '\n'; //? 1 - return space_base(x, space_index(x), Current_routine->calls.front().default_space); -} - -long long int space_base(const reagent& x, long long int space_index, long long int base) { -//? trace("foo") << "base of space " << space_index << '\n'; //? 1 -//? cerr << "space_base sub: " << x.to_string() << '\n'; //? 1 - if (space_index == 0) { -//? trace("foo") << "base of space " << space_index << " is " << base << '\n'; //? 1 - return base; - } -//? trace("foo") << "base of space " << space_index << " is " << Memory[base+1] << '\n'; //? 1 - long long int result = space_base(x, space_index-1, Memory[base+1]); - return result; -} - -long long int space_index(const reagent& x) { -//? cerr << "space_index: " << x.to_string() << '\n'; //? 1 - for (long long int i = /*skip name:type*/1; i < SIZE(x.properties); ++i) { - if (x.properties.at(i).first == "space") { - assert(SIZE(x.properties.at(i).second) == 1); - return to_integer(x.properties.at(i).second.at(0)); - } - } - return 0; -} - -:(scenario permit_space_as_variable_name) -recipe main [ - space:number <- copy 0:literal -] diff --git a/045closure_name.cc b/045closure_name.cc deleted file mode 100644 index a9a7575c..00000000 --- a/045closure_name.cc +++ /dev/null @@ -1,133 +0,0 @@ -//: Writing to a literal (not computed) address of 0 in a recipe chains two -//: spaces together. When a variable has a property of /space:1, it looks up -//: the variable in the chained/surrounding space. /space:2 looks up the -//: surrounding space of the surrounding space, etc. - -:(scenario closure) -recipe main [ - default-space:address:array:location <- new location:type, 30:literal - 1:address:array:location/names:new-counter <- new-counter -#? $print [AAAAAAAAAAAAAAAA] -#? $print 1:address:array:location - 2:number/raw <- increment-counter 1:address:array:location/names:new-counter - 3:number/raw <- increment-counter 1:address:array:location/names:new-counter -] - -recipe new-counter [ - default-space:address:array:location <- new location:type, 30:literal - x:number <- copy 23:literal - y:number <- copy 3:literal # variable that will be incremented - reply default-space:address:array:location -] - -recipe increment-counter [ - default-space:address:array:location <- new location:type, 30:literal - 0:address:array:location/names:new-counter <- next-ingredient # outer space must be created by 'new-counter' above - y:number/space:1 <- add y:number/space:1, 1:literal # increment - y:number <- copy 234:literal # dummy - reply y:number/space:1 -] - -+name: recipe increment-counter is surrounded by new-counter -+mem: storing 5 in location 3 - -//: To make this work, compute the recipe that provides names for the -//: surrounding space of each recipe. This must happen before transform_names. - -:(before "End Globals") -map Surrounding_space; - -:(after "int main") - Transform.push_back(collect_surrounding_spaces); - -:(code) -void collect_surrounding_spaces(const recipe_ordinal r) { - for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { - const instruction& inst = Recipe[r].steps.at(i); - if (inst.is_label) continue; - for (long long int j = 0; j < SIZE(inst.products); ++j) { - if (is_literal(inst.products.at(j))) continue; - if (inst.products.at(j).name != "0") continue; - if (SIZE(inst.products.at(j).types) != 3 - || inst.products.at(j).types.at(0) != Type_ordinal["address"] - || inst.products.at(j).types.at(1) != Type_ordinal["array"] - || inst.products.at(j).types.at(2) != Type_ordinal["location"]) { - raise << "slot 0 should always have type address:array:location, but is " << inst.products.at(j).to_string() << '\n'; - continue; - } - vector s = property(inst.products.at(j), "names"); - if (s.empty()) - raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << die(); - if (SIZE(s) > 1) raise << "slot 0 should have a single value in /names, got " << inst.products.at(j).to_string() << '\n'; - string surrounding_recipe_name = s.at(0); - if (Surrounding_space.find(r) != Surrounding_space.end() - && Surrounding_space[r] != Recipe_ordinal[surrounding_recipe_name]) { - raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n'; - continue; - } - trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name; - Surrounding_space[r] = Recipe_ordinal[surrounding_recipe_name]; - } - } -} - -//: Once surrounding spaces are available, transform_names uses them to handle -//: /space properties. - -:(replace{} "long long int lookup_name(const reagent& r, const recipe_ordinal default_recipe)") -long long int lookup_name(const reagent& x, const recipe_ordinal default_recipe) { -//? cout << "AAA " << default_recipe << " " << Recipe[default_recipe].name << '\n'; //? 2 -//? cout << "AAA " << x.to_string() << '\n'; //? 1 - if (!has_property(x, "space")) { - if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << die(); - return Name[default_recipe][x.name]; - } - vector p = property(x, "space"); - if (SIZE(p) != 1) raise << "/space property should have exactly one (non-negative integer) value\n"; - long long int n = to_integer(p.at(0)); - assert(n >= 0); - recipe_ordinal surrounding_recipe = lookup_surrounding_recipe(default_recipe, n); - set done; - vector path; - return lookup_name(x, surrounding_recipe, done, path); -} - -// If the recipe we need to lookup this name in doesn't have names done yet, -// recursively call transform_names on it. -long long int lookup_name(const reagent& x, const recipe_ordinal r, set& done, vector& path) { - if (!Name[r].empty()) return Name[r][x.name]; - if (done.find(r) != done.end()) { - raise << "can't compute address of " << x.to_string() << " because "; - for (long long int i = 1; i < SIZE(path); ++i) { - raise << path.at(i-1) << " requires computing names of " << path.at(i) << '\n'; - } - raise << path.at(SIZE(path)-1) << " requires computing names of " << r << "..ad infinitum\n" << die(); - return 0; - } - done.insert(r); - path.push_back(r); - transform_names(r); // Not passing 'done' through. Might this somehow cause an infinite loop? - assert(!Name[r].empty()); - return Name[r][x.name]; -} - -recipe_ordinal lookup_surrounding_recipe(const recipe_ordinal r, long long int n) { - if (n == 0) return r; - if (Surrounding_space.find(r) == Surrounding_space.end()) { - raise << "don't know surrounding recipe of " << Recipe[r].name << '\n'; - return 0; - } - assert(Surrounding_space[r]); - return lookup_surrounding_recipe(Surrounding_space[r], n-1); -} - -//: weaken use-before-set warnings just a tad -:(replace{} "bool already_transformed(const reagent& r, const map& names)") -bool already_transformed(const reagent& r, const map& names) { - if (has_property(r, "space")) { - vector p = property(r, "space"); - assert(SIZE(p) == 1); - if (p.at(0) != "0") return true; - } - return names.find(r.name) != names.end(); -} diff --git a/045space_surround.cc b/045space_surround.cc new file mode 100644 index 00000000..61521013 --- /dev/null +++ b/045space_surround.cc @@ -0,0 +1,60 @@ +//: So far you can have global variables by not setting default-space, and +//: local variables by setting default-space. You can isolate variables +//: between those extremes by creating 'surrounding' spaces. +//: +//: (Surrounding spaces are like lexical scopes in other languages.) + +:(scenario surrounding_space) +# location 1 in space 1 refers to the space surrounding the default space, here 20. +recipe main [ + 10:number <- copy 5:literal # pretend array + 20:number <- copy 5:literal # pretend array + default-space:address:array:location <- copy 10:literal + 0:address:array:location/names:dummy <- copy 20:literal # later layers will explain the /names: property + 1:number <- copy 32:literal + 1:number/space:1 <- copy 33:literal +] +# chain space ++mem: storing 20 in location 11 +# store to default-space ++mem: storing 32 in location 12 +# store to chained space ++mem: storing 33 in location 22 + +//: If you think of a space as a collection of variables with a common +//: lifetime, surrounding allows managing shorter lifetimes inside a longer +//: one. + +:(replace{} "long long int space_base(const reagent& x)") +long long int space_base(const reagent& x) { +//? cerr << "space_base: " << x.to_string() << '\n'; //? 1 + return space_base(x, space_index(x), Current_routine->calls.front().default_space); +} + +long long int space_base(const reagent& x, long long int space_index, long long int base) { +//? trace("foo") << "base of space " << space_index << '\n'; //? 1 +//? cerr << "space_base sub: " << x.to_string() << '\n'; //? 1 + if (space_index == 0) { +//? trace("foo") << "base of space " << space_index << " is " << base << '\n'; //? 1 + return base; + } +//? trace("foo") << "base of space " << space_index << " is " << Memory[base+1] << '\n'; //? 1 + long long int result = space_base(x, space_index-1, Memory[base+1]); + return result; +} + +long long int space_index(const reagent& x) { +//? cerr << "space_index: " << x.to_string() << '\n'; //? 1 + for (long long int i = /*skip name:type*/1; i < SIZE(x.properties); ++i) { + if (x.properties.at(i).first == "space") { + assert(SIZE(x.properties.at(i).second) == 1); + return to_integer(x.properties.at(i).second.at(0)); + } + } + return 0; +} + +:(scenario permit_space_as_variable_name) +recipe main [ + space:number <- copy 0:literal +] diff --git a/046closure_name.cc b/046closure_name.cc new file mode 100644 index 00000000..a9a7575c --- /dev/null +++ b/046closure_name.cc @@ -0,0 +1,133 @@ +//: Writing to a literal (not computed) address of 0 in a recipe chains two +//: spaces together. When a variable has a property of /space:1, it looks up +//: the variable in the chained/surrounding space. /space:2 looks up the +//: surrounding space of the surrounding space, etc. + +:(scenario closure) +recipe main [ + default-space:address:array:location <- new location:type, 30:literal + 1:address:array:location/names:new-counter <- new-counter +#? $print [AAAAAAAAAAAAAAAA] +#? $print 1:address:array:location + 2:number/raw <- increment-counter 1:address:array:location/names:new-counter + 3:number/raw <- increment-counter 1:address:array:location/names:new-counter +] + +recipe new-counter [ + default-space:address:array:location <- new location:type, 30:literal + x:number <- copy 23:literal + y:number <- copy 3:literal # variable that will be incremented + reply default-space:address:array:location +] + +recipe increment-counter [ + default-space:address:array:location <- new location:type, 30:literal + 0:address:array:location/names:new-counter <- next-ingredient # outer space must be created by 'new-counter' above + y:number/space:1 <- add y:number/space:1, 1:literal # increment + y:number <- copy 234:literal # dummy + reply y:number/space:1 +] + ++name: recipe increment-counter is surrounded by new-counter ++mem: storing 5 in location 3 + +//: To make this work, compute the recipe that provides names for the +//: surrounding space of each recipe. This must happen before transform_names. + +:(before "End Globals") +map Surrounding_space; + +:(after "int main") + Transform.push_back(collect_surrounding_spaces); + +:(code) +void collect_surrounding_spaces(const recipe_ordinal r) { + for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { + const instruction& inst = Recipe[r].steps.at(i); + if (inst.is_label) continue; + for (long long int j = 0; j < SIZE(inst.products); ++j) { + if (is_literal(inst.products.at(j))) continue; + if (inst.products.at(j).name != "0") continue; + if (SIZE(inst.products.at(j).types) != 3 + || inst.products.at(j).types.at(0) != Type_ordinal["address"] + || inst.products.at(j).types.at(1) != Type_ordinal["array"] + || inst.products.at(j).types.at(2) != Type_ordinal["location"]) { + raise << "slot 0 should always have type address:array:location, but is " << inst.products.at(j).to_string() << '\n'; + continue; + } + vector s = property(inst.products.at(j), "names"); + if (s.empty()) + raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << die(); + if (SIZE(s) > 1) raise << "slot 0 should have a single value in /names, got " << inst.products.at(j).to_string() << '\n'; + string surrounding_recipe_name = s.at(0); + if (Surrounding_space.find(r) != Surrounding_space.end() + && Surrounding_space[r] != Recipe_ordinal[surrounding_recipe_name]) { + raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n'; + continue; + } + trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name; + Surrounding_space[r] = Recipe_ordinal[surrounding_recipe_name]; + } + } +} + +//: Once surrounding spaces are available, transform_names uses them to handle +//: /space properties. + +:(replace{} "long long int lookup_name(const reagent& r, const recipe_ordinal default_recipe)") +long long int lookup_name(const reagent& x, const recipe_ordinal default_recipe) { +//? cout << "AAA " << default_recipe << " " << Recipe[default_recipe].name << '\n'; //? 2 +//? cout << "AAA " << x.to_string() << '\n'; //? 1 + if (!has_property(x, "space")) { + if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << die(); + return Name[default_recipe][x.name]; + } + vector p = property(x, "space"); + if (SIZE(p) != 1) raise << "/space property should have exactly one (non-negative integer) value\n"; + long long int n = to_integer(p.at(0)); + assert(n >= 0); + recipe_ordinal surrounding_recipe = lookup_surrounding_recipe(default_recipe, n); + set done; + vector path; + return lookup_name(x, surrounding_recipe, done, path); +} + +// If the recipe we need to lookup this name in doesn't have names done yet, +// recursively call transform_names on it. +long long int lookup_name(const reagent& x, const recipe_ordinal r, set& done, vector& path) { + if (!Name[r].empty()) return Name[r][x.name]; + if (done.find(r) != done.end()) { + raise << "can't compute address of " << x.to_string() << " because "; + for (long long int i = 1; i < SIZE(path); ++i) { + raise << path.at(i-1) << " requires computing names of " << path.at(i) << '\n'; + } + raise << path.at(SIZE(path)-1) << " requires computing names of " << r << "..ad infinitum\n" << die(); + return 0; + } + done.insert(r); + path.push_back(r); + transform_names(r); // Not passing 'done' through. Might this somehow cause an infinite loop? + assert(!Name[r].empty()); + return Name[r][x.name]; +} + +recipe_ordinal lookup_surrounding_recipe(const recipe_ordinal r, long long int n) { + if (n == 0) return r; + if (Surrounding_space.find(r) == Surrounding_space.end()) { + raise << "don't know surrounding recipe of " << Recipe[r].name << '\n'; + return 0; + } + assert(Surrounding_space[r]); + return lookup_surrounding_recipe(Surrounding_space[r], n-1); +} + +//: weaken use-before-set warnings just a tad +:(replace{} "bool already_transformed(const reagent& r, const map& names)") +bool already_transformed(const reagent& r, const map& names) { + if (has_property(r, "space")) { + vector p = property(r, "space"); + assert(SIZE(p) == 1); + if (p.at(0) != "0") return true; + } + return names.find(r.name) != names.end(); +} diff --git a/046tangle.cc b/046tangle.cc deleted file mode 100644 index 0c4129f0..00000000 --- a/046tangle.cc +++ /dev/null @@ -1,198 +0,0 @@ -//: Allow code for recipes to be pulled in from multiple places. -//: -//: TODO: switch recipe.steps to a more efficient data structure. - -:(scenario tangle_before) -recipe main [ - 1:number <- copy 0:literal - +label1 - 3:number <- copy 0:literal -] - -before +label1 [ - 2:number <- copy 0:literal -] -+mem: storing 0 in location 1 -+mem: storing 0 in location 2 -+mem: storing 0 in location 3 -# nothing else -$mem: 3 - -//: while loading recipes, load before/after fragments - -:(before "End Globals") -map Before_fragments, After_fragments; -:(before "End Setup") -Before_fragments.clear(); -After_fragments.clear(); - -:(before "End Command Handlers") -else if (command == "before") { - string label = next_word(in); - recipe tmp = slurp_recipe(in); - Before_fragments[label].steps.insert(Before_fragments[label].steps.end(), tmp.steps.begin(), tmp.steps.end()); -} -else if (command == "after") { - string label = next_word(in); - recipe tmp = slurp_recipe(in); - After_fragments[label].steps.insert(After_fragments[label].steps.begin(), tmp.steps.begin(), tmp.steps.end()); -} - -//: after all recipes are loaded, insert fragments at appropriate labels - -:(after "int main") - Transform.push_back(insert_fragments); - -:(code) -void insert_fragments(const recipe_ordinal r) { - // Copy into a new vector because insertions invalidate iterators. - // But this way we can't insert into labels created inside before/after. - vector result; - for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { - const instruction inst = Recipe[r].steps.at(i); - if (!inst.is_label) { - result.push_back(inst); - continue; - } - if (Before_fragments.find(inst.label) != Before_fragments.end()) { - result.insert(result.end(), Before_fragments[inst.label].steps.begin(), Before_fragments[inst.label].steps.end()); - } - result.push_back(inst); - if (After_fragments.find(inst.label) != After_fragments.end()) { - result.insert(result.end(), After_fragments[inst.label].steps.begin(), After_fragments[inst.label].steps.end()); - } - } -//? for (long long int i = 0; i < SIZE(result); ++i) { //? 1 -//? cout << result.at(i).to_string() << '\n'; //? 1 -//? } //? 1 - Recipe[r].steps.swap(result); -} - -:(scenario tangle_before_and_after) -recipe main [ - 1:number <- copy 0:literal - +label1 - 4:number <- copy 0:literal -] -before +label1 [ - 2:number <- copy 0:literal -] -after +label1 [ - 3:number <- copy 0:literal -] -+mem: storing 0 in location 1 -+mem: storing 0 in location 2 -# label1 -+mem: storing 0 in location 3 -+mem: storing 0 in location 4 -# nothing else -$mem: 4 - -:(scenario tangle_keeps_labels_separate) -recipe main [ - 1:number <- copy 0:literal - +label1 - +label2 - 6:number <- copy 0:literal -] -before +label1 [ - 2:number <- copy 0:literal -] -after +label1 [ - 3:number <- copy 0:literal -] -before +label2 [ - 4:number <- copy 0:literal -] -after +label2 [ - 5:number <- copy 0:literal -] -+mem: storing 0 in location 1 -+mem: storing 0 in location 2 -# label1 -+mem: storing 0 in location 3 -# 'after' fragments for earlier label always go before 'before' fragments for later label -+mem: storing 0 in location 4 -# label2 -+mem: storing 0 in location 5 -+mem: storing 0 in location 6 -# nothing else -$mem: 6 - -:(scenario tangle_stacks_multiple_fragments) -recipe main [ - 1:number <- copy 0:literal - +label1 - 6:number <- copy 0:literal -] -before +label1 [ - 2:number <- copy 0:literal -] -after +label1 [ - 3:number <- copy 0:literal -] -before +label1 [ - 4:number <- copy 0:literal -] -after +label1 [ - 5:number <- copy 0:literal -] -+mem: storing 0 in location 1 -# 'before' fragments stack in order -+mem: storing 0 in location 2 -+mem: storing 0 in location 4 -# label1 -# 'after' fragments stack in reverse order -+mem: storing 0 in location 5 -+mem: storing 0 in location 3 -+mem: storing 0 in location 6 -# nothing else -$mem: 6 - -:(scenario tangle_supports_fragments_with_multiple_instructions) -recipe main [ - 1:number <- copy 0:literal - +label1 - 6:number <- copy 0:literal -] -before +label1 [ - 2:number <- copy 0:literal - 3:number <- copy 0:literal -] -after +label1 [ - 4:number <- copy 0:literal - 5:number <- copy 0:literal -] -+mem: storing 0 in location 1 -+mem: storing 0 in location 2 -+mem: storing 0 in location 3 -# label1 -+mem: storing 0 in location 4 -+mem: storing 0 in location 5 -+mem: storing 0 in location 6 -# nothing else -$mem: 6 - -:(scenario tangle_tangles_into_all_labels_with_same_name) -recipe main [ - 1:number <- copy 0:literal - +label1 - +label1 - 4:number <- copy 0:literal -] -before +label1 [ - 2:number <- copy 0:literal -] -after +label1 [ - 3:number <- copy 0:literal -] -+mem: storing 0 in location 1 -+mem: storing 0 in location 2 -# label1 -+mem: storing 0 in location 3 -+mem: storing 0 in location 2 -# label1 -+mem: storing 0 in location 3 -+mem: storing 0 in location 4 -# nothing else -$mem: 6 diff --git a/047jump_label.cc b/047jump_label.cc deleted file mode 100644 index dca2e37a..00000000 --- a/047jump_label.cc +++ /dev/null @@ -1,110 +0,0 @@ -//: Support jumps to labels. -//: We'll also treat 'break' and 'continue' as jumps. The choice of name is -//: just documentation about intent. - -:(scenario jump_to_label) -recipe main [ - jump +target:label - 1:number <- copy 0:literal - +target -] --mem: storing 0 in location 1 - -:(before "End Mu Types Initialization") -Type_ordinal["label"] = 0; - -:(after "int main") - Transform.push_back(transform_labels); - -:(code) -void transform_labels(const recipe_ordinal r) { - map offset; - for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { - const instruction& inst = Recipe[r].steps.at(i); - if (!inst.label.empty()) offset[inst.label] = i; - } - for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { - instruction& inst = Recipe[r].steps.at(i); - if (inst.operation == Recipe_ordinal["jump"]) { -//? cerr << inst.to_string() << '\n'; //? 1 - replace_offset(inst.ingredients.at(0), offset, i, r); - } - if (inst.operation == Recipe_ordinal["jump-if"] || inst.operation == Recipe_ordinal["jump-unless"]) { - replace_offset(inst.ingredients.at(1), offset, i, r); - } - if ((inst.operation == Recipe_ordinal["loop"] || inst.operation == Recipe_ordinal["break"]) - && SIZE(inst.ingredients) == 1) { - replace_offset(inst.ingredients.at(0), offset, i, r); - } - if ((inst.operation == Recipe_ordinal["loop-if"] || inst.operation == Recipe_ordinal["loop-unless"] - || inst.operation == Recipe_ordinal["break-if"] || inst.operation == Recipe_ordinal["break-unless"]) - && SIZE(inst.ingredients) == 2) { - replace_offset(inst.ingredients.at(1), offset, i, r); - } - } -} - -:(code) -void replace_offset(reagent& x, /*const*/ map& offset, const long long int current_offset, const recipe_ordinal r) { -//? cerr << "AAA " << x.to_string() << '\n'; //? 1 - assert(is_literal(x)); -//? cerr << "BBB " << x.to_string() << '\n'; //? 1 - assert(!x.initialized); -//? cerr << "CCC " << x.to_string() << '\n'; //? 1 - if (is_integer(x.name)) return; // non-labels will be handled like other number operands -//? cerr << "DDD " << x.to_string() << '\n'; //? 1 - if (offset.find(x.name) == offset.end()) - raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n'; - x.set_value(offset[x.name]-current_offset); -} - -:(scenario break_to_label) -recipe main [ -#? $print [aaa] - { - { - break +target:label - 1:number <- copy 0:literal - } - } - +target -] --mem: storing 0 in location 1 - -:(scenario jump_if_to_label) -recipe main [ - { - { - jump-if 1:literal, +target:label - 1:number <- copy 0:literal - } - } - +target -] --mem: storing 0 in location 1 - -:(scenario loop_unless_to_label) -recipe main [ - { - { - loop-unless 0:literal, +target:label # loop/break with a label don't care about braces - 1:number <- copy 0:literal - } - } - +target -] --mem: storing 0 in location 1 - -:(scenario jump_runs_code_after_label) -recipe main [ - # first a few lines of padding to exercise the offset computation - 1:number <- copy 0:literal - 2:number <- copy 0:literal - 3:number <- copy 0:literal - jump +target:label - 4:number <- copy 0:literal - +target - 5:number <- copy 0:literal -] -+mem: storing 0 in location 5 --mem: storing 0 in location 4 diff --git a/048tangle.cc b/048tangle.cc new file mode 100644 index 00000000..0c4129f0 --- /dev/null +++ b/048tangle.cc @@ -0,0 +1,198 @@ +//: Allow code for recipes to be pulled in from multiple places. +//: +//: TODO: switch recipe.steps to a more efficient data structure. + +:(scenario tangle_before) +recipe main [ + 1:number <- copy 0:literal + +label1 + 3:number <- copy 0:literal +] + +before +label1 [ + 2:number <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 ++mem: storing 0 in location 3 +# nothing else +$mem: 3 + +//: while loading recipes, load before/after fragments + +:(before "End Globals") +map Before_fragments, After_fragments; +:(before "End Setup") +Before_fragments.clear(); +After_fragments.clear(); + +:(before "End Command Handlers") +else if (command == "before") { + string label = next_word(in); + recipe tmp = slurp_recipe(in); + Before_fragments[label].steps.insert(Before_fragments[label].steps.end(), tmp.steps.begin(), tmp.steps.end()); +} +else if (command == "after") { + string label = next_word(in); + recipe tmp = slurp_recipe(in); + After_fragments[label].steps.insert(After_fragments[label].steps.begin(), tmp.steps.begin(), tmp.steps.end()); +} + +//: after all recipes are loaded, insert fragments at appropriate labels + +:(after "int main") + Transform.push_back(insert_fragments); + +:(code) +void insert_fragments(const recipe_ordinal r) { + // Copy into a new vector because insertions invalidate iterators. + // But this way we can't insert into labels created inside before/after. + vector result; + for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { + const instruction inst = Recipe[r].steps.at(i); + if (!inst.is_label) { + result.push_back(inst); + continue; + } + if (Before_fragments.find(inst.label) != Before_fragments.end()) { + result.insert(result.end(), Before_fragments[inst.label].steps.begin(), Before_fragments[inst.label].steps.end()); + } + result.push_back(inst); + if (After_fragments.find(inst.label) != After_fragments.end()) { + result.insert(result.end(), After_fragments[inst.label].steps.begin(), After_fragments[inst.label].steps.end()); + } + } +//? for (long long int i = 0; i < SIZE(result); ++i) { //? 1 +//? cout << result.at(i).to_string() << '\n'; //? 1 +//? } //? 1 + Recipe[r].steps.swap(result); +} + +:(scenario tangle_before_and_after) +recipe main [ + 1:number <- copy 0:literal + +label1 + 4:number <- copy 0:literal +] +before +label1 [ + 2:number <- copy 0:literal +] +after +label1 [ + 3:number <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 ++mem: storing 0 in location 4 +# nothing else +$mem: 4 + +:(scenario tangle_keeps_labels_separate) +recipe main [ + 1:number <- copy 0:literal + +label1 + +label2 + 6:number <- copy 0:literal +] +before +label1 [ + 2:number <- copy 0:literal +] +after +label1 [ + 3:number <- copy 0:literal +] +before +label2 [ + 4:number <- copy 0:literal +] +after +label2 [ + 5:number <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 +# 'after' fragments for earlier label always go before 'before' fragments for later label ++mem: storing 0 in location 4 +# label2 ++mem: storing 0 in location 5 ++mem: storing 0 in location 6 +# nothing else +$mem: 6 + +:(scenario tangle_stacks_multiple_fragments) +recipe main [ + 1:number <- copy 0:literal + +label1 + 6:number <- copy 0:literal +] +before +label1 [ + 2:number <- copy 0:literal +] +after +label1 [ + 3:number <- copy 0:literal +] +before +label1 [ + 4:number <- copy 0:literal +] +after +label1 [ + 5:number <- copy 0:literal +] ++mem: storing 0 in location 1 +# 'before' fragments stack in order ++mem: storing 0 in location 2 ++mem: storing 0 in location 4 +# label1 +# 'after' fragments stack in reverse order ++mem: storing 0 in location 5 ++mem: storing 0 in location 3 ++mem: storing 0 in location 6 +# nothing else +$mem: 6 + +:(scenario tangle_supports_fragments_with_multiple_instructions) +recipe main [ + 1:number <- copy 0:literal + +label1 + 6:number <- copy 0:literal +] +before +label1 [ + 2:number <- copy 0:literal + 3:number <- copy 0:literal +] +after +label1 [ + 4:number <- copy 0:literal + 5:number <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 ++mem: storing 0 in location 3 +# label1 ++mem: storing 0 in location 4 ++mem: storing 0 in location 5 ++mem: storing 0 in location 6 +# nothing else +$mem: 6 + +:(scenario tangle_tangles_into_all_labels_with_same_name) +recipe main [ + 1:number <- copy 0:literal + +label1 + +label1 + 4:number <- copy 0:literal +] +before +label1 [ + 2:number <- copy 0:literal +] +after +label1 [ + 3:number <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 ++mem: storing 0 in location 4 +# nothing else +$mem: 6 -- cgit 1.4.1-2-gfad0