diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2016-08-20 17:51:58 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2016-08-20 17:51:58 -0700 |
commit | bc98ddb2b699682dffb5590f9c5e5b2bf36cb278 (patch) | |
tree | a614f3c0f669d9b3129c9b0e65f0f27ce99be860 | |
parent | 10f415a60a130ceefc4ce0f32155507763e6cee6 (diff) | |
download | mu-bc98ddb2b699682dffb5590f9c5e5b2bf36cb278.tar.gz |
3229 - fake file systems using 'assume-filesystem'
Built with Stephen Malina.
-rw-r--r-- | 082scenario_screen.cc | 9 | ||||
-rw-r--r-- | 085scenario_console.cc | 4 | ||||
-rw-r--r-- | 088file.mu | 7 | ||||
-rw-r--r-- | 089scenario_filesystem.cc | 210 |
4 files changed, 223 insertions, 7 deletions
diff --git a/082scenario_screen.cc b/082scenario_screen.cc index 006547f9..5443a369 100644 --- a/082scenario_screen.cc +++ b/082scenario_screen.cc @@ -116,10 +116,6 @@ scenario screen-in-scenario-color [ ] +error: expected screen location (0, 0) to be in color 2 instead of 1 -//: allow naming just for 'screen' -:(before "End is_special_name Cases") -if (s == "screen") return true; - :(scenarios run) :(scenario convert_names_does_not_fail_when_mixing_special_names_and_numeric_locations) % Scenario_testing_scenario = true; @@ -148,8 +144,13 @@ assert(Name[tmp_recipe.at(0)][""] < Max_variables_in_scenarios); // Scenario Globals. const int SCREEN = Next_predefined_global_for_scenarios++; // End Scenario Globals. + +//: give 'screen' a fixed location in scenarios :(before "End Special Scenario Variable Names(r)") Name[r]["screen"] = SCREEN; +//: make 'screen' always a raw location in scenarios +:(before "End is_special_name Cases") +if (s == "screen") return true; :(before "End Rewrite Instruction(curr, recipe result)") // rewrite `assume-screen width, height` to diff --git a/085scenario_console.cc b/085scenario_console.cc index b6f820f1..c1f75735 100644 --- a/085scenario_console.cc +++ b/085scenario_console.cc @@ -35,10 +35,10 @@ scenario keyboard-in-scenario [ :(before "End Scenario Globals") const int CONSOLE = Next_predefined_global_for_scenarios++; +//: give 'console' a fixed location in scenarios :(before "End Special Scenario Variable Names(r)") Name[r]["console"] = CONSOLE; - -//: allow naming just for 'console' +//: make 'console' always a raw location in scenarios :(before "End is_special_name Cases") if (s == "console") return true; diff --git a/088file.mu b/088file.mu index b26ae438..4e3a6dec 100644 --- a/088file.mu +++ b/088file.mu @@ -2,7 +2,12 @@ # are thus easier to test. container filesystem [ - {data: (address table (address array character) (address array character))} + data:address:array:file-mapping +] + +container file-mapping [ + name:address:array:character + contents:address:array:character ] def start-reading fs:address:filesystem, filename:address:array:character -> contents:address:source:character [ diff --git a/089scenario_filesystem.cc b/089scenario_filesystem.cc new file mode 100644 index 00000000..0c3d1202 --- /dev/null +++ b/089scenario_filesystem.cc @@ -0,0 +1,210 @@ +//: Clean syntax to manipulate and check the file system in scenarios. +//: Instructions 'assume-filesystem' and 'filesystem-should-contain' implicitly create +//: a variable called 'filesystem' that is accessible to later instructions in +//: the scenario. 'filesystem-should-contain' can check unicode characters in +//: the fake filesystem + +:(scenarios run_mu_scenario) +:(scenario simple_filesystem) +scenario assume-filesystem [ + local-scope + assume-filesystem [ + # file 'a' containing two lines of data + [a] <- [ + |a bc| + |de f| + ] + # directory 'b' containing two files, 'c' and 'd' + [b/c] <- [] + [b/d] <- [ + |xyz| + ] + ] + data:address:array:file-mapping <- get *filesystem:address:filesystem, data:offset + file1:file-mapping <- index *data, 0 + file1-name:address:array:character <- get file1, name:offset + 10:array:character/raw <- copy *file1-name + file1-contents:address:array:character <- get file1, contents:offset + 100:array:character/raw <- copy *file1-contents + file2:file-mapping <- index *data, 1 + file2-name:address:array:character <- get file2, name:offset + 30:array:character/raw <- copy *file2-name + file2-contents:address:array:character <- get file2, contents:offset + 40:array:character/raw <- copy *file2-contents + file3:file-mapping <- index *data, 2 + file3-name:address:array:character <- get file3, name:offset + 50:array:character/raw <- copy *file3-name + file3-contents:address:array:character <- get file3, contents:offset + 60:array:character/raw <- copy *file3-contents + memory-should-contain [ + 10:array:character <- [a] + 100:array:character <- [a bc +de f +] + 30:array:character <- [b/c] + 40:array:character <- [] + 50:array:character <- [b/d] + 60:array:character <- [xyz +] + ] +] + +:(before "End Globals") +const int FILESYSTEM = Next_predefined_global_for_scenarios++; +//: give 'filesystem' a fixed location in scenarios +:(before "End Special Scenario Variable Names(r)") +Name[r]["filesystem"] = FILESYSTEM; +//: make 'filesystem' always a raw location in scenarios +:(before "End is_special_name Cases") +if (s == "filesystem") return true; + +:(before "End initialize_transform_rewrite_literal_string_to_text()") +recipes_taking_literal_strings.insert("assume-filesystem"); + +//: screen-should-contain is a regular instruction +:(before "End Primitive Recipe Declarations") +ASSUME_FILESYSTEM, +:(before "End Primitive Recipe Numbers") +put(Recipe_ordinal, "assume-filesystem", ASSUME_FILESYSTEM); +:(before "End Primitive Recipe Checks") +case ASSUME_FILESYSTEM: { + break; +} +:(before "End Primitive Recipe Implementations") +case ASSUME_FILESYSTEM: { + assert(scalar(ingredients.at(0))); + assume_filesystem(current_instruction().ingredients.at(0).name, current_recipe_name()); + break; +} + +:(code) +void assume_filesystem(const string& data, const string& caller) { + map<string, string> contents; + parse_filesystem(data, contents, caller); + construct_filesystem_object(contents); +} + +void parse_filesystem(const string& data, map<string, string>& out, const string& caller) { + istringstream in(data); + in >> std::noskipws; + while (true) { + if (!has_data(in)) break; + skip_whitespace_and_comments(in); + if (!has_data(in)) break; + string filename = next_filesystem_word(in); + if (*filename.begin() != '[') { + raise << caller << ": assume-filesystem: filename '" << filename << "' must begin with a '['\n" << end(); + break; + } + if (*filename.rbegin() != ']') { + raise << caller << ": assume-filesystem: filename '" << filename << "' must end with a ']'\n" << end(); + break; + } + filename.erase(0, 1); + filename.erase(SIZE(filename)-1); + if (!has_data(in)) { + raise << caller << ": assume-filesystem: no data for filename '" << filename << "'\n" << end(); + break; + } + string arrow = next_filesystem_word(in); + if (arrow != "<-") { + raise << caller << ": assume-filesystem: expected '<-' after filename '" << filename << "' but got '" << arrow << "'\n" << end(); + break; + } + if (!has_data(in)) { + raise << caller << ": assume-filesystem: no data for filename '" << filename << "' after '<-'\n" << end(); + break; + } + string contents = next_filesystem_word(in); + if (*contents.begin() != '[') { + raise << caller << ": assume-filesystem: file contents '" << contents << "' for filename '" << filename << "' must begin with a '['\n" << end(); + break; + } + if (*contents.rbegin() != ']') { + raise << caller << ": assume-filesystem: file contents '" << contents << "' for filename '" << filename << "' must end with a ']'\n" << end(); + break; + } + contents.erase(0, 1); + contents.erase(SIZE(contents)-1); + put(out, filename, munge_filesystem_contents(contents, filename, caller)); + } +} + +string munge_filesystem_contents(const string& data, const string& filename, const string& caller) { + if (data.empty()) return ""; + istringstream in(data); + in >> std::noskipws; + skip_whitespace_and_comments(in); + ostringstream out; + while (true) { + if (!has_data(in)) break; + skip_whitespace(in); + if (!has_data(in)) break; + if (in.peek() != '|') { + raise << caller << ": assume-filesystem: file contents for filename '" << filename << "' must be delimited in '|'s\n" << end(); + break; + } + in.get(); // skip leading '|' + string line; + getline(in, line); + for (int i = 0; i < SIZE(line); ++i) { + // todo: handle escaped '|' + if (line.at(i) == '|') break; + out << line.at(i); + } + // todo: some way to represent a file without a final newline + out << '\n'; + } + return out.str(); +} + +void construct_filesystem_object(const map<string, string>& contents) { + int filesystem_data_address = allocate(SIZE(contents)*2 + /*array length*/1); + int curr = filesystem_data_address + /*skip refcount*/1 + /*skip array length*/1; + for (map<string, string>::const_iterator p = contents.begin(); p != contents.end(); ++p) { + put(Memory, curr, new_mu_string(p->first)); + curr++; + put(Memory, curr, new_mu_string(p->second)); + curr++; + } + put(Memory, filesystem_data_address+/*skip refcount*/1, SIZE(contents)); // size of array + put(Memory, filesystem_data_address, 1); // initialize refcount + // wrap the filesystem data in a filesystem object + int filesystem_address = allocate(size_of_filesystem()); + put(Memory, filesystem_address+/*skip refcount*/1, filesystem_data_address); + put(Memory, filesystem_address, 1); // initialize refcount + // save in product + put(Memory, FILESYSTEM, filesystem_address); +} + +int size_of_filesystem() { + // memoize result if already computed + static int result = 0; + if (result) return result; + assert(get(Type_ordinal, "filesystem")); + type_tree* type = new type_tree("filesystem"); + result = size_of(type)+/*refcount*/1; + delete type; + return result; +} + +string next_filesystem_word(istream& in) { + skip_whitespace_and_comments(in); + if (in.peek() == '[') { + string result = slurp_quoted(in); + skip_whitespace_and_comments_but_not_newline(in); + return result; + } + ostringstream out; + slurp_word(in, out); + skip_whitespace_and_comments(in); + return out.str(); +} + +void skip_whitespace(istream& in) { + while (true) { + if (!has_data(in)) break; + if (isspace(in.peek())) in.get(); + else break; + } +} |