//: Interacting with the file system. //: '$open-file-for-reading' returns a FILE* as a number (ugh) //: '$read-from-file' accepts a number, interprets it as a FILE* (double ugh) and reads a character from it //: Similarly for writing files. //: These interfaces are ugly and tied to the current (Linux) host Mu happens //: to be implemented atop. Later layers will wrap them with better, more //: testable interfaces. //: //: Clearly we don't care about performance or any of that so far. //: todo: reading/writing binary files :(before "End Primitive Recipe Declarations") _OPEN_FILE_FOR_READING, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$open-file-for-reading", _OPEN_FILE_FOR_READING); :(before "End Primitive Recipe Checks") case _OPEN_FILE_FOR_READING: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'$open-file-for-reading' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); break; } if (!is_mu_text(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$open-file-for-reading' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } if (SIZE(inst.products) != 1) { raise << maybe(get(Recipe, r).name) << "'$open-file-for-reading' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); break; } if (!is_mu_number(inst.products.at(0))) { raise << maybe(get(Recipe, r).name) << "first product of '$open-file-for-reading' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _OPEN_FILE_FOR_READING: { string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1)); assert(sizeof(long long int) >= sizeof(FILE*)); FILE* f = fopen(filename.c_str(), "r"); long long int result = reinterpret_cast(f); products.resize(1); products.at(0).push_back(static_cast(result)); break; } :(before "End Primitive Recipe Declarations") _OPEN_FILE_FOR_WRITING, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$open-file-for-writing", _OPEN_FILE_FOR_WRITING); :(before "End Primitive Recipe Checks") case _OPEN_FILE_FOR_WRITING: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'$open-file-for-writing' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); break; } if (!is_mu_text(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$open-file-for-writing' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } if (SIZE(inst.products) != 1) { raise << maybe(get(Recipe, r).name) << "'$open-file-for-writing' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); break; } if (!is_mu_number(inst.products.at(0))) { raise << maybe(get(Recipe, r).name) << "first product of '$open-file-for-writing' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _OPEN_FILE_FOR_WRITING: { string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1)); assert(sizeof(long long int) >= sizeof(FILE*)); long long int result = reinterpret_cast(fopen(filename.c_str(), "w")); products.resize(1); products.at(0).push_back(static_cast(result)); break; } :(before "End Primitive Recipe Declarations") _READ_FROM_FILE, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "$read-from-file", _READ_FROM_FILE); :(before "End Primitive Recipe Checks") case _READ_FROM_FILE: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); break; } if (!is_mu_number(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); break; } if (SIZE(inst.products) != 2) { raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly two products, but got '" << to_original_string(inst) << "'\n" << end(); break; } if (!is_mu_character(inst.products.at(0))) { raise << maybe(get(Recipe, r).name) << "first product of '$read-from-file' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end(); break; } if (!is_mu_boolean(inst.products.at(1))) { raise << maybe(get(Recipe, r).name) << "second product of '$read-from-file' should be a boolean, but got '" << to_string(inst.products.at(1)) << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case _READ_FROM_FILE: { long long int x = static_cast(ingredients.at(0).at(0)); FILE* f = reinterpret_cast(x); if (f == NULL) { raise << maybe(current_recipe_name()) << "can't read from null file in '" << to_string(current_instruction()) << "'\n" << end(); break; } products.resize(2); if (feof(f)) { products.at(0).push_back(0); products.at(1).push_back(1); // eof break; } if (ferror(f)) { raise << maybe(current_recipe_name()) << "file in invalid state in '" << to_string(