https://github.com/akkartik/mu/blob/master/019type_abbreviations.cc

      1 //: For convenience, allow Mu types to be abbreviated.
      2
      3 :(scenarios transform)
      4 :(scenario type_abbreviations)
      5 type foo = number
      6 def main [
      7   a:foo <- copy 34
      8 ]
      9 +transform: product type after expanding abbreviations: "number"
     10
     11 :(before "End Globals")
     12 map<string, type_tree*> Type_abbreviations, Type_abbreviations_snapshot;
     13
     14 //:: Defining type abbreviations.
     15
     16 :(before "End Command Handlers")
     17 else if (command == "type") {
     18   load_type_abbreviations(in);
     19 }
     20
     21 :(code)
     22 void load_type_abbreviations(istream& in) {
     23   string new_type_name = next_word(in);
     24   assert(has_data(in) || !new_type_name.empty());
     25   if (!has_data(in) || new_type_name.empty()) {
     26     raise << "incomplete 'type' statement; must be of the form 'type <new type name> = <type expression>'\n" << end();
     27     return;
     28   }
     29   string arrow = next_word(in);
     30   assert(has_data(in) || !arrow.empty());
     31   if (arrow.empty()) {
     32     raise << "incomplete 'type' statement 'type " << new_type_name << "'\n" << end();
     33     return;
     34   }
     35   if (arrow != "=") {
     36     raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
     37     return;
     38   }
     39   if (!has_data(in)) {
     40     raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
     41     return;
     42   }
     43   string old = next_word(in);
     44   if (old.empty()) {
     45     raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
     46     raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
     47     return;
     48   }
     49   if (contains_key(Type_abbreviations, new_type_name)) {
     50     raise << "'type' conflict: '" << new_type_name << "' defined as both '" << names_to_string_without_quotes(get(Type_abbreviations, new_type_name)) << "' and '" << old << "'\n" << end();
     51     return;
     52   }
     53   trace(9990, "type") << "alias " << new_type_name << " = " << old << end();
     54   type_tree* old_type = new_type_tree(old);
     55   put(Type_abbreviations, new_type_name, old_type);
     56 }
     57
     58 type_tree* new_type_tree(const string& x) {
     59   string_tree* type_names = starts_with(x, "(") ? parse_string_tree(x) : parse_string_list(x);
     60   type_tree* result = new_type_tree(type_names);
     61   delete type_names;
     62   expand_type_abbreviations(result);
     63   return result;
     64 }
     65
     66 string_tree* parse_string_list(const string& s) {
     67   istringstream in(s);
     68   in >> std::noskipws;
     69   return parse_property_list(in);
     70 }
     71
     72 :(scenario type_error1)
     73 % Hide_errors = true;
     74 type foo
     75 +error: incomplete 'type' statement 'type foo'
     76
     77 :(scenario type_error2)
     78 % Hide_errors = true;
     79 type foo =
     80 +error: incomplete 'type' statement 'type foo ='
     81
     82 :(scenario type_error3)
     83 % Hide_errors = true;
     84 type foo bar baz
     85 +error: 'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type foo bar'
     86
     87 :(scenario type_conflict_error)
     88 % Hide_errors = true;
     89 type foo = bar
     90 type foo = baz
     91 +error: 'type' conflict: 'foo' defined as both 'bar' and 'baz'
     92
     93 :(scenario type_abbreviation_for_compound)
     94 type foo = address:number
     95 def main [
     96   1:foo <- copy null
     97 ]
     98 +transform: product type after expanding abbreviations: ("address" "number")
     99
    100 //: cleaning up type abbreviations between tests and before exiting
    101
    102 :(before "End save_snapshots")
    103 Type_abbreviations_snapshot = Type_abbreviations;
    104 :(before "End restore_snapshots")
    105 restore_type_abbreviations();
    106 :(before "End One-time Setup")
    107 atexit(clear_type_abbreviations);
    108 :(code)
    109 void restore_type_abbreviations() {
    110   for (map<string, type_tree*>::iterator p = Type_abbreviations.begin();  p != Type_abbreviations.end();  ++p) {
    111     if (!contains_key(Type_abbreviations_snapshot, p->first))
    112       delete p->second;
    113   }
    114   Type_abbreviations.clear();
    115   Type_abbreviations = Type_abbreviations_snapshot;
    116 }
    117 void clear_type_abbreviations() {
    118   for (map<string, type_tree*>::iterator p = Type_abbreviations.begin();  p != Type_abbreviations.end();  ++p)
    119     delete p->second;
    120   Type_abbreviations.clear();
    121 }
    122
    123 //:: A few default abbreviations.
    124
    125 :(before "End Mu Types Initialization")
    126 put(Type_abbreviations, "&", new_type_tree("address"));
    127 put(Type_abbreviations, "@", new_type_tree("array"));
    128 put(Type_abbreviations, "num", new_type_tree("number"));
    129 put(Type_abbreviations, "bool", new_type_tree("boolean"));
    130 put(Type_abbreviations, "char", new_type_tree("character"));
    131
    132 :(scenario use_type_abbreviations_when_declaring_type_abbreviations)
    133 type foo = &:num
    134 def main [
    135   1:foo <- copy null
    136 ]
    137 +transform: product type after expanding abbreviations: ("address" "number")
    138
    139 //:: Expand type aliases before running.
    140 //: We'll do this in a transform so that we don't need to define abbreviations
    141 //: before we use them.
    142
    143 :(scenario abbreviations_for_address_and_array)
    144 def main [
    145   f 1:&:num  # abbreviation for 'address:number'
    146   f 2:@:num  # abbreviation for 'array:number'
    147   f 3:&:@:num  # combining '&' and '@'
    148   f 4:&:&:@:&:@:num  # ..any number of times
    149   f {5: (array (& num) 3)}  # support for dilated reagents and more complex parse trees
    150 ]
    151 def f [
    152 ]
    153 +transform: --- expand type abbreviations in recipe 'main'
    154 +transform: ingredient type after expanding abbreviations: ("address" "number")
    155 +transform: ingredient type after expanding abbreviations: ("array" "number")
    156 +transform: ingredient type after expanding abbreviations: ("address" "array" "number")
    157 +transform: ingredient type after expanding abbreviations: ("address" "address" "array" "address" "array" "number")
    158 +transform: ingredient type after expanding abbreviations: ("array" ("address" "number") "3")
    159
    160 :(before "Transform.push_back(update_instruction_operations)")
    161 Transform.push_back(expand_type_abbreviations);  // idempotent
    162 // Begin Type Modifying Transforms
    163 // End Type Modifying Transforms
    164
    165 :(code)
    166 void expand_type_abbreviations(const recipe_ordinal r) {
    167   expand_type_abbreviations(get(Recipe, r));
    168 }
    169
    170 void expand_type_abbreviations(const recipe& caller) {
    171   trace(9991, "transform") << "--- expand type abbreviations in recipe '" << caller.name << "'" << end();
    172   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
    173     const instruction& inst = caller.steps.at(i);
    174     trace(9991, "transform") << "instruction '" << to_original_string(inst) << end();
    175     for (long int i = 0;  i < SIZE(inst.ingredients);  ++i) {
    176       expand_type_abbreviations(inst.ingredients.at(i).type);
    177       trace(9992, "transform") << "ingredient type after expanding abbreviations: " << names_to_string(inst.ingredients.at(i).type) << end();
    178     }
    179     for (long int i = 0;  i < SIZE(inst.products);  ++i) {
    180       expand_type_abbreviations(inst.products.at(i).type);
    181       trace(9992, "transform") << "product type after expanding abbreviations: " << names_to_string(inst.products.at(i).type) << end();
    182     }
    183   }
    184   // End Expand Type Abbreviations(caller)
    185 }
    186
    187 void expand_type_abbreviations(type_tree* type) {
    188   if (!type) return;
    189   if (!type->atom) {
    190     expand_type_abbreviations(type->left);
    191     expand_type_abbreviations(type->right);
    192     return;
    193   }
    194   if (contains_key(Type_abbreviations, type->name))
    195     *type = type_tree(*get(Type_abbreviations, type->name));
    196 }