//: Phase 1 of running Mu code: load it from a textual representation. //: //: The process of running Mu code: //: load -> transform -> run :(scenarios load) // use 'load' instead of 'run' in all scenarios in this layer :(scenario first_recipe) def main [ 1:number <- copy 23 ] +parse: instruction: copy +parse: ingredient: {23: "literal"} +parse: product: {1: "number"} :(code) vector load(string form) { istringstream in(form); in >> std::noskipws; return load(in); } vector load(istream& in) { in >> std::noskipws; vector result; while (has_data(in)) { skip_whitespace_and_comments(in); if (!has_data(in)) break; string command = next_word(in); if (command.empty()) { assert(!has_data(in)); break; } // Command Handlers if (command == "recipe" || command == "def") { recipe_ordinal r = slurp_recipe(in); if (r > 0) result.push_back(r); } else if (command == "recipe!" || command == "def!") { Disable_redefine_checks = true; recipe_ordinal r = slurp_recipe(in); if (r > 0) result.push_back(r); Disable_redefine_checks = false; } // End Command Handlers else { raise << "unknown top-level command: " << command << '\n' << end(); } } return result; } // return the recipe ordinal slurped, or -1 if it failed int slurp_recipe(istream& in) { recipe result; result.name = next_word(in); if (result.name.empty()) { assert(!has_data(in)); raise << "file ended with 'recipe'\n" << end(); return -1; } // End Load Recipe Name skip_whitespace_but_not_newline(in); // End Recipe Refinements if (result.name.empty()) raise << "empty result.name\n" << end(); trace(9991, "parse") << "--- defining " << result.name << end(); if (!contains_key(Recipe_ordinal, result.name)) put(Recipe_ordinal, result.name, Next_recipe_ordinal); result.ordinal = get(Recipe_ordinal, result.name); ++Next_recipe_ordinal; if (Recipe.find(get(Recipe_ordinal, result.name)) != Recipe.end()) { trace(9991, "parse") << "already exists" << end(); if (should_check_for_redefine(result.name)) raise << "redefining recipe " << result.name << "\n" << end(); Recipe.erase(get(Recipe_ordinal, result.name)); } slurp_body(in, result); // End Recipe Body(result) put(Recipe, get(Recipe_ordinal, result.name), result); return get(Recipe_ordinal, result.name); } void slurp_body(istream& in, recipe& result) { in >> std::noskipws; skip_whitespace_but_not_newline(in); if (in.get() != '[') raise << result.name << ": recipe body must begin with '['\n" << end(); skip_whitespace_and_comments(in); // permit trailing comment after '[' instruction curr; while (next_instruction(in, &curr)) { curr.original_string = to_original_string(curr); // End Rewrite Instruction(curr, recipe result) trace(9992, "load") << "after rewriting: " << to_string(curr) << end(); if (!curr.is_empty()) result.steps.push_back(curr); } } bool next_instruction(istream& in, instruction* curr) { curr->clear(); skip_whitespace_and_comments(in); if (!has_data(in)) { raise << "incomplete recipe at end of file (0)\n" << end(); return false; } vector words; while (has_data(in) && in.peek() != '\n') { skip_whitespace_but_not_newline(in); if (!has_data(in)) { raise << "incomplete recipe at end of file (1)\n" << end(); return false; } string word = next_word(in); if (word.empty()) { assert(!has_data(in)); raise << "incomplete recipe at end of file (2)\n" << end(); return false; } words.push_back(word); skip_whitespace_but_not_newline(in); } skip_whitespace_and_comments(in); if (SIZE(words) == 1 && words.at(0) == "]") return false; // end of recipe if (SIZE(words) == 1 && is_label_word(words.at(0))) { curr->is_label = true; curr->label = words.at(0); trace(9993, "parse") << "label: " << curr->label << end(); if (!has_data(in)) { raise << "incomplete recipe at end of file (3)\n" << end(); return false; } return true; } vector::iterator p = words.begin(); if (find(words.begin(), words.end(), "<-") != words.end()) { for (; *p != "<-"; ++p) curr->products.push_back(reagent(*p)); ++p; // skip <- } if (p == words.end()) { raise << "instruction prematurely ended with '<-'\n" << end(); return false; } curr->name = *p; ++p; // curr->operation will be set at transform time for (; p != words.end(); ++p) curr->ingredients.push_back(reagent(*p)); trace(9993, "parse") << "instruction: " << curr->name << end(); trace(9993, "parse") << " number of ingredients: " << SIZE(curr->ingredients) << end(); for (vector::iterator p = curr->ingredients.begin(); p != curr->ingredients.end(); ++p) trace(9993, "parse") << " ingredient: " << to_string(*p) << end(); for (vector::iterator p = curr->products.begin(); p != curr->products.end(); ++p) trace(9993, "parse") << " product: " << to_string(*p) << end(); if (!has_data(in)) { raise << "9: unbalanced '[' for recipe\n" << end(); return false; } // End next_instruction(curr) return true; } // can return empty string -- only if 'in' has no more data string next_word(istream& in) { skip_whitespace_but_not_newline(in); // End next_word Special-cases ostringstream out; slurp_wor
0.6.0
=====

- Allow moving vertical window positions (/titlebar, /mainwin, /statusbar, /inputwin)
- Allow loading/unloading all pluguns (/plugins)
- Allow installing plugins from directory (/plugins)
- Theme option for status bar time (statusbar.time)
- Case/accent insensitive autocompletion
- Shift tab to select previous autocomplete suggestion
- Allow searching help (/hel