about summary refs log tree commit diff stats
path: root/019type_abbreviations.cc
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-06-17 16:10:00 -0700
committerKartik Agaram <vc@akkartik.com>2018-06-17 16:10:00 -0700
commit92a3d0824b37e564f3d5bb7e042f97f991f25416 (patch)
treeb4a8f79805307ed1a1d9428a184fd4a633f15054 /019type_abbreviations.cc
parent01ce563dfe3e6cf58337708b9dbb60a8a99fa0f2 (diff)
downloadmu-92a3d0824b37e564f3d5bb7e042f97f991f25416.tar.gz
4263
Implement literal constants before type abbreviations, reducing some
unnecessary tangling.
Diffstat (limited to '019type_abbreviations.cc')
-rw-r--r--019type_abbreviations.cc196
1 files changed, 196 insertions, 0 deletions
diff --git a/019type_abbreviations.cc b/019type_abbreviations.cc
new file mode 100644
index 00000000..df797dfa
--- /dev/null
+++ b/019type_abbreviations.cc
@@ -0,0 +1,196 @@
+//: For convenience, allow Mu types to be abbreviated.
+
+:(scenarios transform)
+:(scenario type_abbreviations)
+type foo = number
+def main [
+  a:foo <- copy 34
+]
++transform: product type after expanding abbreviations: "number"
+
+:(before "End Globals")
+map<string, type_tree*> Type_abbreviations, Type_abbreviations_snapshot;
+
+//:: Defining type abbreviations.
+
+:(before "End Command Handlers")
+else if (command == "type") {
+  load_type_abbreviations(in);
+}
+
+:(code)
+void load_type_abbreviations(istream& in) {
+  string new_type_name = next_word(in);
+  assert(has_data(in) || !new_type_name.empty());
+  if (!has_data(in) || new_type_name.empty()) {
+    raise << "incomplete 'type' statement; must be of the form 'type <new type name> = <type expression>'\n" << end();
+    return;
+  }
+  string arrow = next_word(in);
+  assert(has_data(in) || !arrow.empty());
+  if (arrow.empty()) {
+    raise << "incomplete 'type' statement 'type " << new_type_name << "'\n" << end();
+    return;
+  }
+  if (arrow != "=") {
+    raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
+    return;
+  }
+  if (!has_data(in)) {
+    raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
+    return;
+  }
+  string old = next_word(in);
+  if (old.empty()) {
+    raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
+    raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
+    return;
+  }
+  if (contains_key(Type_abbreviations, new_type_name)) {
+    raise << "'type' conflict: '" << new_type_name << "' defined as both '" << names_to_string_without_quotes(get(Type_abbreviations, new_type_name)) << "' and '" << old << "'\n" << end();
+    return;
+  }
+  trace(9990, "type") << "alias " << new_type_name << " = " << old << end();
+  type_tree* old_type = new_type_tree(old);
+  put(Type_abbreviations, new_type_name, old_type);
+}
+
+type_tree* new_type_tree(const string& x) {
+  string_tree* type_names = starts_with(x, "(") ? parse_string_tree(x) : parse_string_list(x);
+  type_tree* result = new_type_tree(type_names);
+  delete type_names;
+  expand_type_abbreviations(result);
+  return result;
+}
+
+string_tree* parse_string_list(const string& s) {
+  istringstream in(s);
+  in >> std::noskipws;
+  return parse_property_list(in);
+}
+
+:(scenario type_error1)
+% Hide_errors = true;
+type foo
++error: incomplete 'type' statement 'type foo'
+
+:(scenario type_error2)
+% Hide_errors = true;
+type foo =
++error: incomplete 'type' statement 'type foo ='
+
+:(scenario type_error3)
+% Hide_errors = true;
+type foo bar baz
++error: 'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type foo bar'
+
+:(scenario type_conflict_error)
+% Hide_errors = true;
+type foo = bar
+type foo = baz
++error: 'type' conflict: 'foo' defined as both 'bar' and 'baz'
+
+:(scenario type_abbreviation_for_compound)
+type foo = address:number
+def main [
+  1:foo <- copy null
+]
++transform: product type after expanding abbreviations: ("address" "number")
+
+//: cleaning up type abbreviations between tests and before exiting
+
+:(before "End save_snapshots")
+Type_abbreviations_snapshot = Type_abbreviations;
+:(before "End restore_snapshots")
+restore_type_abbreviations();
+:(before "End One-time Setup")
+atexit(clear_type_abbreviations);
+:(code)
+void restore_type_abbreviations() {
+  for (map<string, type_tree*>::iterator p = Type_abbreviations.begin();  p != Type_abbreviations.end();  ++p) {
+    if (!contains_key(Type_abbreviations_snapshot, p->first))
+      delete p->second;
+  }
+  Type_abbreviations.clear();
+  Type_abbreviations = Type_abbreviations_snapshot;
+}
+void clear_type_abbreviations() {
+  for (map<string, type_tree*>::iterator p = Type_abbreviations.begin();  p != Type_abbreviations.end();  ++p)
+    delete p->second;
+  Type_abbreviations.clear();
+}
+
+//:: A few default abbreviations.
+
+:(before "End Mu Types Initialization")
+put(Type_abbreviations, "&", new_type_tree("address"));
+put(Type_abbreviations, "@", new_type_tree("array"));
+put(Type_abbreviations, "num", new_type_tree("number"));
+put(Type_abbreviations, "bool", new_type_tree("boolean"));
+put(Type_abbreviations, "char", new_type_tree("character"));
+
+:(scenario use_type_abbreviations_when_declaring_type_abbreviations)
+type foo = &:num
+def main [
+  1:foo <- copy null
+]
++transform: product type after expanding abbreviations: ("address" "number")
+
+//:: Expand type aliases before running.
+//: We'll do this in a transform so that we don't need to define abbreviations
+//: before we use them.
+
+:(scenario abbreviations_for_address_and_array)
+def main [
+  f 1:&:num  # abbreviation for 'address:number'
+  f 2:@:num  # abbreviation for 'array:number'
+  f 3:&:@:num  # combining '&' and '@'
+  f 4:&:&:@:&:@:num  # ..any number of times
+  f {5: (array (& num) 3)}  # support for dilated reagents and more complex parse trees
+]
+def f [
+]
++transform: --- expand type abbreviations in recipe 'main'
++transform: ingredient type after expanding abbreviations: ("address" "number")
++transform: ingredient type after expanding abbreviations: ("array" "number")
++transform: ingredient type after expanding abbreviations: ("address" "array" "number")
++transform: ingredient type after expanding abbreviations: ("address" "address" "array" "address" "array" "number")
++transform: ingredient type after expanding abbreviations: ("array" ("address" "number") "3")
+
+:(before "Transform.push_back(update_instruction_operations)")
+Transform.push_back(expand_type_abbreviations);  // idempotent
+// Begin Type Modifying Transforms
+// End Type Modifying Transforms
+
+:(code)
+void expand_type_abbreviations(const recipe_ordinal r) {
+  expand_type_abbreviations(get(Recipe, r));
+}
+
+void expand_type_abbreviations(const recipe& caller) {
+  trace(9991, "transform") << "--- expand type abbreviations in recipe '" << caller.name << "'" << end();
+  for (int i = 0;  i < SIZE(caller.steps);  ++i) {
+    const instruction& inst = caller.steps.at(i);
+    trace(9991, "transform") << "instruction '" << to_original_string(inst) << end();
+    for (long int i = 0;  i < SIZE(inst.ingredients);  ++i) {
+      expand_type_abbreviations(inst.ingredients.at(i).type);
+      trace(9992, "transform") << "ingredient type after expanding abbreviations: " << names_to_string(inst.ingredients.at(i).type) << end();
+    }
+    for (long int i = 0;  i < SIZE(inst.products);  ++i) {
+      expand_type_abbreviations(inst.products.at(i).type);
+      trace(9992, "transform") << "product type after expanding abbreviations: " << names_to_string(inst.products.at(i).type) << end();
+    }
+  }
+  // End Expand Type Abbreviations(caller)
+}
+
+void expand_type_abbreviations(type_tree* type) {
+  if (!type) return;
+  if (!type->atom) {
+    expand_type_abbreviations(type->left);
+    expand_type_abbreviations(type->right);
+    return;
+  }
+  if (contains_key(Type_abbreviations, type->name))
+    *type = type_tree(*get(Type_abbreviations, type->name));
+}