diff options
Diffstat (limited to 'archive/2.vm/011load.cc')
-rw-r--r-- | archive/2.vm/011load.cc | 489 |
1 files changed, 0 insertions, 489 deletions
diff --git a/archive/2.vm/011load.cc b/archive/2.vm/011load.cc deleted file mode 100644 index 4bb06c36..00000000 --- a/archive/2.vm/011load.cc +++ /dev/null @@ -1,489 +0,0 @@ -//: Phase 1 of running Mu code: load it from a textual representation. -//: -//: The process of running Mu code: -//: load -> transform -> run - -void test_first_recipe() { - load( - "def main [\n" - " 1:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -vector<recipe_ordinal> load(string form) { - istringstream in(form); - in >> std::noskipws; - return load(in); -} - -vector<recipe_ordinal> load(istream& in) { - in >> std::noskipws; - vector<recipe_ordinal> 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(101, "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(101, "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(102, "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<string> 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(103, "parse") << "label: " << curr->label << end(); - if (!has_data(in)) { - raise << "incomplete recipe at end of file (3)\n" << end(); - return false; - } - return true; - } - - vector<string>::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(103, "parse") << "instruction: " << curr->name << end(); - trace(103, "parse") << " number of ingredients: " << SIZE(curr->ingredients) << end(); - for (vector<reagent>::iterator p = curr->ingredients.begin(); p != curr->ingredients.end(); ++p) - trace(103, "parse") << " ingredient: " << to_string(*p) << end(); - for (vector<reagent>::iterator p = curr->products.begin(); p != curr->products.end(); ++p) - trace(103, "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_word(in, out); - skip_whitespace_and_comments_but_not_newline(in); - string result = out.str(); - if (result != "[" && ends_with(result, '[')) - raise << "insert a space before '[' in '" << result << "'\n" << end(); - return result; -} - -bool is_label_word(const string& word) { - if (word.empty()) return false; // error raised elsewhere - return !isalnum(word.at(0)) && string("$_*@&,=-[]()").find(word.at(0)) == string::npos; -} - -bool ends_with(const string& s, const char c) { - if (s.empty()) return false; - return *s.rbegin() == c; -} - -:(before "End Globals") -// word boundaries -extern const string Terminators("(){}"); -:(code) -void slurp_word(istream& in, ostream& out) { - char c; - if (has_data(in) && Terminators.find(in.peek()) != string::npos) { - in >> c; - out << c; - return; - } - while (in >> c) { - if (isspace(c) || Terminators.find(c) != string::npos || Ignore.find(c) != string::npos) { - in.putback(c); - break; - } - out << c; - } -} - -void skip_whitespace_and_comments(istream& in) { - while (true) { - if (!has_data(in)) break; - if (isspace(in.peek())) in.get(); - else if (Ignore.find(in.peek()) != string::npos) in.get(); - else if (in.peek() == '#') skip_comment(in); - else break; - } -} - -// confusing; move to the next line only to skip a comment, but never otherwise -void skip_whitespace_and_comments_but_not_newline(istream& in) { - while (true) { - if (!has_data(in)) break; - if (in.peek() == '\n') break; - if (isspace(in.peek())) in.get(); - else if (Ignore.find(in.peek()) != string::npos) in.get(); - else if (in.peek() == '#') skip_comment(in); - else break; - } -} - -void skip_comment(istream& in) { - if (has_data(in) && in.peek() == '#') { - in.get(); - while (has_data(in) && in.peek() != '\n') in.get(); - } -} - -void test_recipe_instead_of_def() { - load( - "recipe main [\n" - " 1:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -void test_parse_comment_outside_recipe() { - load( - "# comment\n" - "def main [\n" - " 1:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -void test_parse_comment_amongst_instruction() { - load( - "def main [\n" - " # comment\n" - " 1:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -void test_parse_comment_amongst_instruction_2() { - load( - "def main [\n" - " # comment\n" - " 1:number <- copy 23\n" - " # comment\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -void test_parse_comment_amongst_instruction_3() { - load( - "def main [\n" - " 1:number <- copy 23\n" - " # comment\n" - " 2:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {2: \"number\"}\n" - ); -} - -void test_parse_comment_after_instruction() { - load( - "def main [\n" - " 1:number <- copy 23 # comment\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -void test_parse_label() { - load( - "def main [\n" - " +foo\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: label: +foo\n" - ); -} - -void test_parse_dollar_as_recipe_name() { - load( - "def main [\n" - " $foo\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: $foo\n" - ); -} - -void test_parse_multiple_properties() { - load( - "def main [\n" - " 1:number <- copy 23/foo:bar:baz\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\", \"foo\": (\"bar\" \"baz\")}\n" - "parse: product: {1: \"number\"}\n" - ); -} - -void test_parse_multiple_products() { - load( - "def main [\n" - " 1:number, 2:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: product: {1: \"number\"}\n" - "parse: product: {2: \"number\"}\n" - ); -} - -void test_parse_multiple_ingredients() { - load( - "def main [\n" - " 1:number, 2:number <- copy 23, 4:number\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: ingredient: {4: \"number\"}\n" - "parse: product: {1: \"number\"}\n" - "parse: product: {2: \"number\"}\n" - ); -} - -void test_parse_multiple_types() { - load( - "def main [\n" - " 1:number, 2:address:number <- copy 23, 4:number\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: instruction: copy\n" - "parse: ingredient: {23: \"literal\"}\n" - "parse: ingredient: {4: \"number\"}\n" - "parse: product: {1: \"number\"}\n" - "parse: product: {2: (\"address\" \"number\")}\n" - ); -} - -void test_parse_properties() { - load( - "def main [\n" - " 1:address:number/lookup <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "parse: product: {1: (\"address\" \"number\"), \"lookup\": ()}\n" - ); -} - -void test_parse_comment_terminated_by_eof() { - load("recipe main [\n" - " a:number <- copy 34\n" - "]\n" - "# abc"); // no newline after comment - cerr << "."; // termination = success -} - -void test_warn_on_missing_space_before_bracket() { - Hide_errors = true; - load( - "def main[\n" - " 1:number <- copy 23\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "error: insert a space before '[' in 'main['\n" - ); -} - -//: Warn if a recipe gets redefined, because large codebases can accidentally -//: step on their own toes. But there'll be many occasions later where -//: we'll want to disable the errors. -:(before "End Globals") -bool Disable_redefine_checks = false; -:(before "End Reset") -Disable_redefine_checks = false; -:(code) -bool should_check_for_redefine(const string& recipe_name) { - if (Disable_redefine_checks) return false; - return true; -} - -void test_forbid_redefining_recipes() { - Hide_errors = true; - load( - "def main [\n" - " 1:number <- copy 23\n" - "]\n" - "def main [\n" - " 1:number <- copy 24\n" - "]\n" - ); - CHECK_TRACE_CONTENTS( - "error: redefining recipe main\n" - ); -} - -void test_permit_forcibly_redefining_recipes() { - load( - "def main [\n" - " 1:number <- copy 23\n" - "]\n" - "def! main [\n" - " 1:number <- copy 24\n" - "]\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("error: redefining recipe main"); - CHECK_TRACE_COUNT("error", 0); -} - -// for debugging -void show_rest_of_stream(istream& in) { - cerr << '^'; - char c; - while (in >> c) - cerr << c; - cerr << "$\n"; - exit(0); -} |