blob: 0c36d5ca135c61f456f0aebfee80e62ac70eedf7 (
plain) (
tree)
|
|
//: A big convenience high-level languages provide is the ability to name memory
//: locations. In mu, a transform called 'convert-names' provides this
//: convenience.
:(scenarios run)
:(scenario "convert_names")
recipe main [
x:integer <- copy 0:literal
]
+name: assign x 1
+run: instruction main/0
+mem: storing 0 in location 1
:(scenario "convert_names_warns")
hide warnings
recipe main [
x:integer <- copy y:integer
]
+warn: use before set: y in main
:(after "int main")
Transform.push_back(transform_names);
:(before "End Globals")
unordered_map<recipe_number, unordered_map<string, int> > Name;
:(before "End One-time Setup")
Name.clear();
:(before "Clear state for recipes_added_by_test")
for (size_t i = 0; i < recipes_added_by_test.size(); ++i) {
Name.erase(recipes_added_by_test[i]);
}
:(code)
void transform_names(const recipe_number r) {
unordered_map<string, int>& names = Name[r];
int curr_idx = 1;
//? cout << "Recipe " << r << '\n'; //? 2
//? cout << Recipe[r].steps.size(); //? 1
for (size_t i = 0; i < Recipe[r].steps.size(); ++i) {
//? cout << "instruction " << i << '\n'; //? 2
instruction& inst = Recipe[r].steps[i];
// Per-recipe Transforms Go Here
// map names to addresses
for (size_t in = 0; in < inst.ingredients.size(); ++in) {
if (is_raw(inst.ingredients[in])) continue;
//? cout << "ingredient " << inst.ingredients[in].name << '\n'; //? 1
if (inst.ingredients[in].name == "default-space")
inst.ingredients[in].initialized = true;
if (inst.ingredients[in].name != "default-space"
&& inst.ingredients[in].types[0]
&& inst.ingredients[in].name.find_first_not_of("0123456789-.") != string::npos) {
if (names.find(inst.ingredients[in].name) == names.end()) {
raise << "use before set: " << inst.ingredients[in].name << " in " << Recipe[r].name;
}
inst.ingredients[in].set_value(names[inst.ingredients[in].name]);
}
}
for (size_t out = 0; out < inst.products.size(); ++out) {
if (is_raw(inst.products[out])) continue;
//? cout << "product " << out << '/' << inst.products.size() << " " << inst.products[out].name << '\n'; //? 4
//? cout << inst.products[out].types[0] << '\n'; //? 1
if (inst.products[out].name == "default-space")
inst.products[out].initialized = true;
if (inst.products[out].name != "_"
&& inst.products[out].name != "default-space"
&& inst.products[out].types[0]
&& inst.products[out].name.find_first_not_of("0123456789-.") != string::npos) {
if (names.find(inst.products[out].name) == names.end()) {
trace("name") << "assign " << inst.products[out].name << " " << curr_idx;
names[inst.products[out].name] = curr_idx;
curr_idx += size_of(inst.products[out]);
}
inst.products[out].set_value(names[inst.products[out].name]);
}
}
}
}
type_number skip_addresses(const vector<type_number>& types) {
for (size_t i = 0; i < types.size(); ++i) {
if (types[i] != Type_number["address"]) return types[i];
}
raise << "expected a record" << '\n' << die();
return -1;
}
int find_element_name(const type_number t, const string& name) {
const type_info& record = Type[t];
//? cout << "looking for field " << name << " in type " << record.name << " with " << record.element_names.size() << " fields\n"; //? 1
for (size_t i = 0; i < record.element_names.size(); ++i) {
if (record.element_names[i] == name) return i;
}
raise << "unknown element " << name << " in record " << t << '\n' << die();
return -1;
}
bool is_raw(const reagent& r) {
for (size_t i = /*skip value+type*/1; i < r.properties.size(); ++i) {
if (r.properties[i].first == "raw") return true;
}
return false;
}
:(scenario "convert_names_passes_dummy")
# _ is just a dummy result that never gets consumed
recipe main [
_, x:integer <- copy 0:literal
]
+name: assign x 1
-name: assign _ 1
//: one reserved word that we'll need later
:(scenario "convert_names_passes_default_space")
recipe main [
default-space:integer, x:integer <- copy 0:literal
]
+name: assign x 1
-name: assign default-space 1
//: an escape hatch to suppress name conversion that we'll use later
:(scenario "convert_names_passes_raw")
recipe main [
x:integer/raw <- copy 0:literal
]
-name: assign x 1
//: update our running example record 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_record_elements")
recipe main [
a:integer <- get 0:point, y:offset
b:integer <- get 0:point, x:offset
]
+name: field y of type point is at offset 1
+name: field x of type point is at offset 0
:(after "Per-recipe Transforms")
// replace element names of records with offsets
if (inst.operation == Recipe_number["get"]
|| inst.operation == Recipe_number["get-address"]) {
// at least 2 args, and second arg is offset
assert(inst.ingredients.size() >= 2);
//? cout << inst.ingredients[1].to_string() << '\n'; //? 1
assert(isa_literal(inst.ingredients[1]));
if (inst.ingredients[1].name.find_first_not_of("0123456789") == string::npos) continue;
// since first non-address in base type must be a record, we don't have to canonize
type_number record = skip_addresses(inst.ingredients[0].types);
inst.ingredients[1].set_value(find_element_name(record, inst.ingredients[1].name));
trace("name") << "field " << inst.ingredients[1].name << " of type " << Type[record].name << " is at offset " << inst.ingredients[1].value;
}
//: this test is actually illegal so can't call run
:(scenarios transform_test)
:(scenario "convert_names_handles_records")
recipe main [
a:point <- copy 0:literal
b:integer <- copy 0:literal
]
+name: assign a 1
+name: assign b 3
|