https://github.com/akkartik/mu1/blob/master/089scenario_filesystem.cc
1
2
3
4
5 :(scenarios run_mu_scenario)
6 :(scenario simple_filesystem)
7 scenario simple-filesystem [
8 local-scope
9 assume-resources [
10
11 [a] <- [
12 |a bc|
13 |de f|
14 ]
15
16 [b/c] <- []
17 [b/d] <- [
18 |xyz|
19 ]
20 ]
21 data:&:@:resource <- get *resources, data:offset
22 file1:resource <- index *data, 0
23 file1-name:text <- get file1, name:offset
24 10:@:char/raw <- copy *file1-name
25 file1-contents:text <- get file1, contents:offset
26 100:@:char/raw <- copy *file1-contents
27 file2:resource <- index *data, 1
28 file2-name:text <- get file2, name:offset
29 30:@:char/raw <- copy *file2-name
30 file2-contents:text <- get file2, contents:offset
31 40:@:char/raw <- copy *file2-contents
32 file3:resource <- index *data, 2
33 file3-name:text <- get file3, name:offset
34 50:@:char/raw <- copy *file3-name
35 file3-contents:text <- get file3, contents:offset
36 60:@:char/raw <- copy *file3-contents
37 memory-should-contain [
38 10:array:character <- [a]
39 100:array:character <- [a bc
40 de f
41 ]
42 30:array:character <- [b/c]
43 40:array:character <- []
44 50:array:character <- [b/d]
45 60:array:character <- [xyz
46 ]
47 ]
48 ]
49
50 :(scenario escaping_file_contents)
51 scenario escaping-file-contents [
52 local-scope
53 assume-resources [
54
55
56 [a] <- [
57 |x\\\\|yz|
58 ]
59 ]
60 data:&:@:resource <- get *resources, data:offset
61 file1:resource <- index *data, 0
62 file1-name:text <- get file1, name:offset
63 10:@:char/raw <- copy *file1-name
64 file1-contents:text <- get file1, contents:offset
65 20:@:char/raw <- copy *file1-contents
66 memory-should-contain [
67 10:array:character <- [a]
68 20:array:character <- [x|yz
69 ]
70 ]
71 ]
72
73 :(before "End Globals")
74 extern const int RESOURCES = next_predefined_global_for_scenarios(2);
75
76 :(before "End Special Scenario Variable Names(r)")
77 Name[r]["resources"] = RESOURCES;
78
79 :(before "End is_special_name Special-cases")
80 if (s == "resources") return true;
81 :(before "End Initialize Type Of Special Name In Scenario(r)")
82 if (r.name == "resources") r.type = new_type_tree("address:resources");
83
84 :(before "End initialize_transform_rewrite_literal_string_to_text()")
85 recipes_taking_literal_strings.insert("assume-resources");
86
87
88 :(before "End Primitive Recipe Declarations")
89 ASSUME_RESOURCES,
90 :(before "End Primitive Recipe Numbers")
91 put(Recipe_ordinal, "assume-resources", ASSUME_RESOURCES);
92 :(before "End Primitive Recipe Checks")
93 case ASSUME_RESOURCES: {
94 break;
95 }
96 :(before "End Primitive Recipe Implementations")
97 case ASSUME_RESOURCES: {
98 assert(scalar(ingredients.at(0)));
99 assume_resources(current_instruction().ingredients.at(0).name, current_recipe_name());
100 break;
101 }
102
103 :(code)
104 void assume_resources(const string& data, const string& caller) {
105 map<string, string> contents;
106 parse_resources(data, contents, caller);
107 construct_resources_object(contents);
108 }
109
110 void parse_resources(const string& data, map<string, string>& out, const string& caller) {
111 istringstream in(data);
112 in >> std::noskipws;
113 while (true) {
114 if (!has_data(in)) break;
115 skip_whitespace_and_comments(in);
116 if (!has_data(in)) break;
117 string filename = next_word(in);
118 if (filename.empty()) {
119 assert(!has_data(in));
120 raise << "incomplete 'resources' block at end of file (0)\n" << end();
121 return;
122 }
123 if (*filename.begin() != '[') {
124 raise << caller << ": assume-resources: filename '" << filename << "' must begin with a '['\n" << end();
125 break;
126 }
127 if (*filename.rbegin() != ']') {
128 raise << caller << ": assume-resources: filename '" << filename << "' must end with a ']'\n" << end();
129 break;
130 }
131 filename.erase(0, 1);
132 filename.erase(SIZE(filename)-1);
133 if (!has_data(in)) {
134 raise << caller << ": assume-resources: no data for filename '" << filename << "'\n" << end();
135 break;
136 }
137 string arrow = next_word(in);
138 if (arrow.empty()) {
139 assert(!has_data(in));
140 raise << "incomplete 'resources' block at end of file (1)\n" << end();
141 return;
142 }
143 if (arrow != "<-") {
144 raise << caller << ": assume-resources: expected '<-' after filename '" << filename << "' but got '" << arrow << "'\n" << end();
145 break;
146 }
147 if (!has_data(in)) {
148 raise << caller << ": assume-resources: no data for filename '" << filename << "' after '<-'\n" << end();
149 break;
150 }
151 string contents = next_word(in);
152 if (contents.empty()) {
153 assert(!has_data(in));
154 raise << "incomplete 'resources' block at end of file (2)\n" << end();
155 return;
156 }
157 if (*contents.begin() != '[') {
158 raise << caller << ": assume-resources: file contents '" << contents << "' for filename '" << filename << "' must begin with a '['\n" << end();
159 break;
160 }
161 if (*contents.rbegin() != ']') {
162 raise << caller << ": assume-resources: file contents '" << contents << "' for filename '" << filename << "' must end with a ']'\n" << end();
163 break;
164 }
165 contents.erase(0, 1);
166 contents.erase(SIZE(contents)-1);
167 put(out, filename, munge_resources_contents(contents, filename, caller));
168 }
169 }
170
171 string munge_resources_contents(const string& data, const string& filename, const string& caller) {
172 if (data.empty()) return "";
173 istringstream in(data);
174 in >> std::noskipws;
175 skip_whitespace_and_comments(in);
176 ostringstream out;
177 while (true) {
178 if (!has_data(in)) break;
179 skip_whitespace(in);
180 if (!has_data(in)) break;
181 if (in.peek() != '|') {
182 raise << caller << ": assume-resources: file contents for filename '" << filename << "' must be delimited in '|'s\n" << end();
183 break;
184 }
185 in.get();
186 string line;
187 getline(in, line);
188 for (int i = 0; i < SIZE(line); ++i) {
189 if (line.at(i) == '|') break;
190 if (line.at(i) == '\\') {
191 ++i;
192 if (i == SIZE(line)) {
193 raise << caller << ": assume-resources: file contents can't end a line with '\\'\n" << end();
194 break;
195 }
196 }
197 out << line.at(i);
198 }
199
200 out << '\n';
201 }
202 return out.str();
203 }
204
205 void construct_resources_object(const map<string, string>& contents) {
206 int resources_data_address = allocate(SIZE(contents) * 4 + 1);
207 int curr = resources_data_address + 1 + 1;
208 for (map<string, string>::const_iterator p = contents.begin(); p != contents.end(); ++p) {
209 ++curr;
210 put(Memory, curr, new_mu_text(p->first));
211 trace("mem") << "storing file name " << get(Memory, curr) << " in location " << curr << end();
212 ++curr;
213 ++curr;
214 put(Memory, curr, new_mu_text(p->second));
215 trace("mem") << "storing file contents " << get(Memory, curr) << " in location " << curr << end();
216 ++curr;
217 }
218 curr = resources_data_address + 1;
219 put(Memory, curr, SIZE(contents));
220 trace("mem") << "storing resources size " << get(Memory, curr) << " in location " << curr << end();
221
222 int resources_address = allocate(size_of_resources());
223 curr = resources_address+1+1+1;
224 put(Memory, curr, resources_data_address);
225 trace("mem") << "storing resources data address " << resources_data_address << " in location " << curr << end();
226
227 put(Memory, RESOURCES+1, resources_address);
228 trace("mem") << "storing resources address " << resources_address << " in location " << RESOURCES << end();
229 }
230
231 int size_of_resources() {
232
233 static int result = 0;
234 if (result) return result;
235 assert(get(Type_ordinal, "resources"));
236 type_tree* type = new type_tree("resources");
237 result = size_of(type);
238 delete type;
239 return result;
240 }