about summary refs log tree commit diff stats
path: root/cpp/011load
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/011load')
-rw-r--r--cpp/011load150
1 files changed, 150 insertions, 0 deletions
diff --git a/cpp/011load b/cpp/011load
new file mode 100644
index 00000000..f2855e5f
--- /dev/null
+++ b/cpp/011load
@@ -0,0 +1,150 @@
+// It's often convenient to express recipes in a textual fashion.
+:(scenarios add_recipe)
+:(scenario first_recipe)
+recipe main [
+  1:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", type: 0}
++parse:   product: {name: "1", type: 1}
+
+:(code)
+int add_recipe(string form) {
+  istringstream in(form);
+  in >> std::noskipws;
+
+  string _recipe = next_word(in);
+  if (_recipe != "recipe")
+    raise << "top-level forms must be of the form 'recipe _name_ [ _instruction_ ... ]'\n";
+
+  string recipe_name = next_word(in);
+  if (recipe_name.empty())
+    raise << "empty recipe name in " << form << '\n';
+  int r = Recipe_number[recipe_name] = Next_recipe_number++;
+
+  if (next_word(in) != "[")
+    raise << "recipe body must begin with '['\n";
+
+  skip_newlines(in);
+
+  instruction curr;
+  while (next_instruction(in, &curr)) {
+    Recipe[r].steps.push_back(curr);
+  }
+  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_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_newlines(in);  if (in.eof()) return false;
+
+  if (words.size() == 1 && *(words[0].end()-1) == ':') {
+    curr->is_label = true;
+    words[0].erase(words[0].end()-1);
+    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->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) {
+  ostringstream out;
+  skip_whitespace(in);
+  slurp_word(in, out);
+  return out.str();
+}
+
+void slurp_word(istream& in, ostream& out) {
+  char c;
+  if (in.peek() == ',') {
+    in >> c;
+    out << c;
+    return;
+  }
+  while (in >> c) {
+    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_newlines(istream& in) {
+  while (in.peek() == '\n')
+    in.get();
+}
+
+void skip_comma(istream& in) {
+  skip_whitespace(in);
+  if (in.peek() == ',') in.get();
+  skip_whitespace(in);
+}
+
+:(scenario parse_label)
+recipe main [
+  foo:
+]
++parse: label: foo
+-parse: instruction: 1
+
+:(scenario parse_multiple_products)
+recipe main [
+  1:integer, 2:integer <- copy 23:literal
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", type: 0}
++parse:   product: {name: "1", type: 1}
++parse:   product: {name: "2", type: 1}
+
+:(scenario parse_multiple_ingredients)
+recipe main [
+  1:integer, 2:integer <- copy 23:literal, 4:integer
+]
++parse: instruction: 1
++parse:   ingredient: {name: "23", type: 0}
++parse:   ingredient: {name: "4", type: 1}
++parse:   product: {name: "1", type: 1}
++parse:   product: {name: "2", type: 1}