diff options
author | Kartik Agaram <vc@akkartik.com> | 2019-07-27 16:01:55 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2019-07-27 17:47:59 -0700 |
commit | 6e1eeeebfb453fa7c871869c19375ce60fbd7413 (patch) | |
tree | 539c4a3fdf1756ae79770d5c4aaf6366f1d1525e /archive/2.vm/087file.cc | |
parent | 8846a7f85cc04b77b2fe8a67b6d317723437b00c (diff) | |
download | mu-6e1eeeebfb453fa7c871869c19375ce60fbd7413.tar.gz |
5485 - promote SubX to top-level
Diffstat (limited to 'archive/2.vm/087file.cc')
-rw-r--r-- | archive/2.vm/087file.cc | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/archive/2.vm/087file.cc b/archive/2.vm/087file.cc new file mode 100644 index 00000000..9fd056db --- /dev/null +++ b/archive/2.vm/087file.cc @@ -0,0 +1,225 @@ +//: 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<long long int>(f); + products.resize(1); + products.at(0).push_back(static_cast<double>(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<long long int>(fopen(filename.c_str(), "w")); + products.resize(1); + products.at(0).push_back(static_cast<double>(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<long long int>(ingredients.at(0).at(0)); + FILE* f = reinterpret_cast<FILE*>(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(current_instruction()) << "'\n" << end(); + break; + } + char c = getc(f); // todo: unicode + if (c == EOF) { + products.at(0).push_back(0); + products.at(1).push_back(1); // eof + break; + } + if (ferror(f)) { + raise << maybe(current_recipe_name()) << "couldn't read from file in '" << to_string(current_instruction()) << "'\n" << end(); + raise << " errno: " << errno << '\n' << end(); + break; + } + products.at(0).push_back(c); + products.at(1).push_back(0); // not eof + break; +} +:(before "End Includes") +#include <errno.h> + +:(before "End Primitive Recipe Declarations") +_WRITE_TO_FILE, +:(before "End Primitive Recipe Numbers") +put(Recipe_ordinal, "$write-to-file", _WRITE_TO_FILE); +:(before "End Primitive Recipe Checks") +case _WRITE_TO_FILE: { + if (SIZE(inst.ingredients) != 2) { + raise << maybe(get(Recipe, r).name) << "'$write-to-file' requires exactly two ingredients, 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 '$write-to-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + break; + } + if (!is_mu_character(inst.ingredients.at(1))) { + raise << maybe(get(Recipe, r).name) << "second ingredient of '$write-to-file' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + break; + } + if (!inst.products.empty()) { + raise << maybe(get(Recipe, r).name) << "'$write-to-file' writes to no products, but got '" << to_original_string(inst) << "'\n" << end(); + break; + } + break; +} +:(before "End Primitive Recipe Implementations") +case _WRITE_TO_FILE: { + long long int x = static_cast<long long int>(ingredients.at(0).at(0)); + FILE* f = reinterpret_cast<FILE*>(x); + if (f == NULL) { + raise << maybe(current_recipe_name()) << "can't write to null file in '" << to_string(current_instruction()) << "'\n" << end(); + break; + } + if (feof(f)) break; + if (ferror(f)) { + raise << maybe(current_recipe_name()) << "file in invalid state in '" << to_string(current_instruction()) << "'\n" << end(); + break; + } + long long int y = static_cast<long long int>(ingredients.at(1).at(0)); + char c = static_cast<char>(y); + putc(c, f); // todo: unicode + if (ferror(f)) { + raise << maybe(current_recipe_name()) << "couldn't write to file in '" << to_string(current_instruction()) << "'\n" << end(); + raise << " errno: " << errno << '\n' << end(); + break; + } + break; +} + +:(before "End Primitive Recipe Declarations") +_CLOSE_FILE, +:(before "End Primitive Recipe Numbers") +put(Recipe_ordinal, "$close-file", _CLOSE_FILE); +:(before "End Primitive Recipe Checks") +case _CLOSE_FILE: { + if (SIZE(inst.ingredients) != 1) { + raise << maybe(get(Recipe, r).name) << "'$close-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 '$close-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); + break; + } + if (SIZE(inst.products) != 1) { + raise << maybe(get(Recipe, r).name) << "'$close-file' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); + break; + } + if (inst.products.at(0).name != inst.ingredients.at(0).name) { + raise << maybe(get(Recipe, r).name) << "'$close-file' requires its product to be the same as its ingredient, but got '" << to_original_string(inst) << "'\n" << end(); + break; + } + break; +} +:(before "End Primitive Recipe Implementations") +case _CLOSE_FILE: { + long long int x = static_cast<long long int>(ingredients.at(0).at(0)); + FILE* f = reinterpret_cast<FILE*>(x); + fclose(f); + products.resize(1); + products.at(0).push_back(0); // todo: ensure that caller always resets the ingredient + break; +} |