about summary refs log tree commit diff stats
path: root/cpp/011load.cc
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/011load.cc')
-rw-r--r--cpp/011load.cc301
1 files changed, 301 insertions, 0 deletions
diff --git a/cpp/011load.cc b/cpp/011load.cc
new file mode 100644
index 00000000..c643a92e
--- /dev/null
+++ b/cpp/011load.cc
@@ -0,0 +1,301 @@
+//: Phase 1 of running mu code: load it from a textual representation.
+
+:(scenarios load)
+:(scenario first_recipe)
+recipe main [
+  1:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
+
+:(code)
+vector<recipe_number> load(string form) {
+  istringstream in(form);
+  in >> std::noskipws;
+  return load(in);
+}
+
+vector<recipe_number> load(istream& in) {
+  vector<recipe_number> result;
+  while (!in.eof()) {
+    skip_comments_and_newlines(in);
+    if (in.eof()) break;
+    string command = next_word(in);
+    // Command Handlers
+    if (command == "recipe") {
+      result.push_back(add_recipe(in));
+    }
+    // End Command Handlers
+    else {
+      raise << "unknown top-level command: " << command << '\n';
+    }
+  }
+  return result;
+}
+
+recipe_number add_recipe(istream& in) {
+  skip_comments_and_newlines(in);
+  string recipe_name = next_word(in);
+//?   cout << "recipe name: ^" << recipe_name << "$\n"; //? 3
+  if (recipe_name.empty())
+    raise << "empty recipe name\n";
+//?     raise << "empty recipe name in " << in.str() << '\n';
+  if (Recipe_number.find(recipe_name) == Recipe_number.end()) {
+    Recipe_number[recipe_name] = Next_recipe_number++;
+//?     cout << "AAA: " << recipe_name << " is now " << Recipe_number[recipe_name] << '\n'; //? 1
+  }
+  recipe_number r = Recipe_number[recipe_name];
+  if (Recipe.find(r) != Recipe.end()) {
+    raise << "redefining recipe " << Recipe[r].name << "\n";
+    Recipe.erase(r);
+  }
+//?   cout << recipe_name << ": adding recipe " << r << '\n'; //? 3
+
+  skip_whitespace(in);
+  if (in.get() != '[')
+    raise << "recipe body must begin with '['\n";
+
+  skip_comments_and_newlines(in);
+
+  instruction curr;
+  while (next_instruction(in, &curr)) {
+//?     if (!curr.products.empty()) cout << curr.products[0].to_string() << '\n'; //? 1
+    Recipe[r].steps.push_back(curr);
+  }
+  Recipe[r].name = recipe_name;
+//?   cout << "recipe " << recipe_name << " has " << Recipe[r].steps.size() << " steps.\n"; //? 1
+  // track added recipes because we may need to undo them in tests; see below
+  recently_added_recipes.push_back(r);
+  return r;
+}
+
+bool next_instruction(istream& in, instruction* curr) {
+  curr->clear();
+  if (in.eof()) return false;
+  skip_whitespace(in);  if (in.eof()) return false;
+  skip_comments_and_newlines(in);  if (in.eof()) return false;
+
+  vector<string> words;
+  while (in.peek() != '\n') {
+    skip_whitespace(in);  if (in.eof()) return false;
+    string word = next_word(in);  if (in.eof()) return false;
+    words.push_back(word);
+    skip_whitespace(in);  if (in.eof()) return false;
+  }
+  skip_comments_and_newlines(in);  if (in.eof()) return false;
+
+//?   if (words.size() == 1) cout << words[0] << ' ' << words[0].size() << '\n'; //? 1
+  if (words.size() == 1 && words[0] == "]") {
+//?     cout << "AAA\n"; //? 1
+    return false;  // end of recipe
+  }
+
+  if (words.size() == 1 && !isalnum(words[0][0])) {
+    curr->is_label = true;
+    curr->label = words[0];
+    trace("parse") << "label: " << curr->label;
+    return !in.eof();
+  }
+
+  vector<string>::iterator p = words.begin();
+  if (find(words.begin(), words.end(), "<-") != words.end()) {
+    for (; *p != "<-"; ++p) {
+      if (*p == ",") continue;
+      curr->products.push_back(reagent(*p));
+    }
+    ++p;  // skip <-
+  }
+
+  curr->name = *p;
+  if (Recipe_number.find(*p) == Recipe_number.end()) {
+    Recipe_number[*p] = Next_recipe_number++;
+//?     cout << "AAA: " << *p << " is now " << Recipe_number[*p] << '\n'; //? 1
+  }
+  if (Recipe_number[*p] == 0) {
+    raise << "Recipe " << *p << " has number 0, which is reserved for IDLE.\n" << die();
+  }
+  curr->operation = Recipe_number[*p];  ++p;
+
+  for (; p != words.end(); ++p) {
+    if (*p == ",") continue;
+    curr->ingredients.push_back(reagent(*p));
+  }
+
+  trace("parse") << "instruction: " << curr->operation;
+  for (vector<reagent>::iterator p = curr->ingredients.begin(); p != curr->ingredients.end(); ++p) {
+    trace("parse") << "  ingredient: " << p->to_string();
+  }
+  for (vector<reagent>::iterator p = curr->products.begin(); p != curr->products.end(); ++p) {
+    trace("parse") << "  product: " << p->to_string();
+  }
+  return !in.eof();
+}
+
+string next_word(istream& in) {
+//?   cout << "AAA next_word\n"; //? 1
+  ostringstream out;
+  skip_whitespace(in);
+  slurp_word(in, out);
+  skip_whitespace(in);
+  skip_comment(in);
+  return out.str();
+}
+
+void slurp_word(istream& in, ostream& out) {
+//?   cout << "AAA slurp_word\n"; //? 1
+  char c;
+  if (in.peek() == ',') {
+    in >> c;
+    out << c;
+    return;
+  }
+  while (in >> c) {
+//?     cout << c << '\n'; //? 1
+    if (isspace(c) || c == ',') {
+      in.putback(c);
+      break;
+    }
+    out << c;
+  }
+}
+
+void skip_whitespace(istream& in) {
+  while (isspace(in.peek()) && in.peek() != '\n') {
+    in.get();
+  }
+}
+
+void skip_comments_and_newlines(istream& in) {
+  while (in.peek() == '\n' || in.peek() == '#') {
+    if (in.peek() == '#') {
+      in.get();
+      while (in.peek() != '\n') in.get();
+    }
+    in.get();
+  }
+}
+
+void skip_comment(istream& in) {
+  if (in.peek() == '#') {
+    in.get();
+    while (in.peek() != '\n') in.get();
+  }
+}
+
+void skip_comma(istream& in) {
+  skip_whitespace(in);
+  if (in.peek() == ',') in.get();
+  skip_whitespace(in);
+}
+
+//: Have tests clean up any recipes they added.
+:(before "End Globals")
+vector<recipe_number> recently_added_recipes;
+:(before "End Setup")
+for (size_t i = 0; i < recently_added_recipes.size(); ++i) {
+//?   cout << "AAA clearing " << Recipe[recently_added_recipes[i]].name << '\n'; //? 2
+  Recipe_number.erase(Recipe[recently_added_recipes[i]].name);
+  Recipe.erase(recently_added_recipes[i]);
+}
+// Clear Other State For recently_added_recipes
+recently_added_recipes.clear();
+
+:(scenario parse_comment_outside_recipe)
+# comment
+recipe main [
+  1:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
+
+:(scenario parse_comment_amongst_instruction)
+recipe main [
+  # comment
+  1:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
+
+:(scenario parse_comment_amongst_instruction2)
+recipe main [
+  # comment
+  1:integer <- copy 23:literal
+  # comment
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
+
+:(scenario parse_comment_amongst_instruction3)
+recipe main [
+  1:integer <- copy 23:literal
+  # comment
+  2:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "2", value: 0, type: 1, properties: ["2": "integer"]}
+
+:(scenario parse_comment_after_instruction)
+recipe main [
+  1:integer <- copy 23:literal  # comment
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
+
+:(scenario parse_label)
+recipe main [
+  +foo
+]
++parse: label: +foo
+-parse: instruction: 1
+
+:(scenario parse_multiple_properties)
+recipe main [
+  1:integer <- copy 23:literal/foo:bar:baz
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal", "foo": "bar":"baz"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
+
+:(scenario parse_multiple_products)
+recipe main [
+  1:integer, 2:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
++parse:   product: {name: "2", value: 0, type: 1, properties: ["2": "integer"]}
+
+:(scenario parse_multiple_ingredients)
+recipe main [
+  1:integer, 2:integer <- copy 23:literal, 4:integer
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   ingredient: {name: "4", value: 0, type: 1, properties: ["4": "integer"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
++parse:   product: {name: "2", value: 0, type: 1, properties: ["2": "integer"]}
+
+:(scenario parse_multiple_types)
+recipe main [
+  1:integer, 2:address:integer <- copy 23:literal, 4:integer
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
++parse:   ingredient: {name: "4", value: 0, type: 1, properties: ["4": "integer"]}
++parse:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
++parse:   product: {name: "2", value: 0, type: 2-1, properties: ["2": "address":"integer"]}
+
+:(scenario parse_properties)
+recipe main [
+  1:integer:address/deref <- copy 23:literal
+]
++parse:   product: {name: "1", value: 0, type: 1-2, properties: ["1": "integer":"address", "deref": ]}