diff options
Diffstat (limited to 'cpp/011load')
-rw-r--r-- | cpp/011load | 150 |
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} |