fix other mandelbrot variants
[mu.git] / archive / 1.vm / 087file.cc
blob9fd056db72a0f3d478d278a6de151903b813f9e7
1 //: Interacting with the file system.
2 //: '$open-file-for-reading' returns a FILE* as a number (ugh)
3 //: '$read-from-file' accepts a number, interprets it as a FILE* (double ugh) and reads a character from it
4 //: Similarly for writing files.
5 //: These interfaces are ugly and tied to the current (Linux) host Mu happens
6 //: to be implemented atop. Later layers will wrap them with better, more
7 //: testable interfaces.
8 //:
9 //: Clearly we don't care about performance or any of that so far.
10 //: todo: reading/writing binary files
12 :(before "End Primitive Recipe Declarations")
13 _OPEN_FILE_FOR_READING,
14 :(before "End Primitive Recipe Numbers")
15 put(Recipe_ordinal, "$open-file-for-reading", _OPEN_FILE_FOR_READING);
16 :(before "End Primitive Recipe Checks")
17 case _OPEN_FILE_FOR_READING: {
18 if (SIZE(inst.ingredients) != 1) {
19 raise << maybe(get(Recipe, r).name) << "'$open-file-for-reading' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
20 break;
22 if (!is_mu_text(inst.ingredients.at(0))) {
23 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();
24 break;
26 if (SIZE(inst.products) != 1) {
27 raise << maybe(get(Recipe, r).name) << "'$open-file-for-reading' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end();
28 break;
30 if (!is_mu_number(inst.products.at(0))) {
31 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();
32 break;
34 break;
36 :(before "End Primitive Recipe Implementations")
37 case _OPEN_FILE_FOR_READING: {
38 string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
39 assert(sizeof(long long int) >= sizeof(FILE*));
40 FILE* f = fopen(filename.c_str(), "r");
41 long long int result = reinterpret_cast<long long int>(f);
42 products.resize(1);
43 products.at(0).push_back(static_cast<double>(result));
44 break;
47 :(before "End Primitive Recipe Declarations")
48 _OPEN_FILE_FOR_WRITING,
49 :(before "End Primitive Recipe Numbers")
50 put(Recipe_ordinal, "$open-file-for-writing", _OPEN_FILE_FOR_WRITING);
51 :(before "End Primitive Recipe Checks")
52 case _OPEN_FILE_FOR_WRITING: {
53 if (SIZE(inst.ingredients) != 1) {
54 raise << maybe(get(Recipe, r).name) << "'$open-file-for-writing' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
55 break;
57 if (!is_mu_text(inst.ingredients.at(0))) {
58 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();
59 break;
61 if (SIZE(inst.products) != 1) {
62 raise << maybe(get(Recipe, r).name) << "'$open-file-for-writing' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end();
63 break;
65 if (!is_mu_number(inst.products.at(0))) {
66 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();
67 break;
69 break;
71 :(before "End Primitive Recipe Implementations")
72 case _OPEN_FILE_FOR_WRITING: {
73 string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1));
74 assert(sizeof(long long int) >= sizeof(FILE*));
75 long long int result = reinterpret_cast<long long int>(fopen(filename.c_str(), "w"));
76 products.resize(1);
77 products.at(0).push_back(static_cast<double>(result));
78 break;
81 :(before "End Primitive Recipe Declarations")
82 _READ_FROM_FILE,
83 :(before "End Primitive Recipe Numbers")
84 put(Recipe_ordinal, "$read-from-file", _READ_FROM_FILE);
85 :(before "End Primitive Recipe Checks")
86 case _READ_FROM_FILE: {
87 if (SIZE(inst.ingredients) != 1) {
88 raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
89 break;
91 if (!is_mu_number(inst.ingredients.at(0))) {
92 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();
93 break;
95 if (SIZE(inst.products) != 2) {
96 raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly two products, but got '" << to_original_string(inst) << "'\n" << end();
97 break;
99 if (!is_mu_character(inst.products.at(0))) {
100 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();
101 break;
103 if (!is_mu_boolean(inst.products.at(1))) {
104 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();
105 break;
107 break;
109 :(before "End Primitive Recipe Implementations")
110 case _READ_FROM_FILE: {
111 long long int x = static_cast<long long int>(ingredients.at(0).at(0));
112 FILE* f = reinterpret_cast<FILE*>(x);
113 if (f == NULL) {
114 raise << maybe(current_recipe_name()) << "can't read from null file in '" << to_string(current_instruction()) << "'\n" << end();
115 break;
117 products.resize(2);
118 if (feof(f)) {
119 products.at(0).push_back(0);
120 products.at(1).push_back(1); // eof
121 break;
123 if (ferror(f)) {
124 raise << maybe(current_recipe_name()) << "file in invalid state in '" << to_string(current_instruction()) << "'\n" << end();
125 break;
127 char c = getc(f); // todo: unicode
128 if (c == EOF) {
129 products.at(0).push_back(0);
130 products.at(1).push_back(1); // eof
131 break;
133 if (ferror(f)) {
134 raise << maybe(current_recipe_name()) << "couldn't read from file in '" << to_string(current_instruction()) << "'\n" << end();
135 raise << " errno: " << errno << '\n' << end();
136 break;
138 products.at(0).push_back(c);
139 products.at(1).push_back(0); // not eof
140 break;
142 :(before "End Includes")
143 #include <errno.h>
145 :(before "End Primitive Recipe Declarations")
146 _WRITE_TO_FILE,
147 :(before "End Primitive Recipe Numbers")
148 put(Recipe_ordinal, "$write-to-file", _WRITE_TO_FILE);
149 :(before "End Primitive Recipe Checks")
150 case _WRITE_TO_FILE: {
151 if (SIZE(inst.ingredients) != 2) {
152 raise << maybe(get(Recipe, r).name) << "'$write-to-file' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
153 break;
155 if (!is_mu_number(inst.ingredients.at(0))) {
156 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();
157 break;
159 if (!is_mu_character(inst.ingredients.at(1))) {
160 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();
161 break;
163 if (!inst.products.empty()) {
164 raise << maybe(get(Recipe, r).name) << "'$write-to-file' writes to no products, but got '" << to_original_string(inst) << "'\n" << end();
165 break;
167 break;
169 :(before "End Primitive Recipe Implementations")
170 case _WRITE_TO_FILE: {
171 long long int x = static_cast<long long int>(ingredients.at(0).at(0));
172 FILE* f = reinterpret_cast<FILE*>(x);
173 if (f == NULL) {
174 raise << maybe(current_recipe_name()) << "can't write to null file in '" << to_string(current_instruction()) << "'\n" << end();
175 break;
177 if (feof(f)) break;
178 if (ferror(f)) {
179 raise << maybe(current_recipe_name()) << "file in invalid state in '" << to_string(current_instruction()) << "'\n" << end();
180 break;
182 long long int y = static_cast<long long int>(ingredients.at(1).at(0));
183 char c = static_cast<char>(y);
184 putc(c, f); // todo: unicode
185 if (ferror(f)) {
186 raise << maybe(current_recipe_name()) << "couldn't write to file in '" << to_string(current_instruction()) << "'\n" << end();
187 raise << " errno: " << errno << '\n' << end();
188 break;
190 break;
193 :(before "End Primitive Recipe Declarations")
194 _CLOSE_FILE,
195 :(before "End Primitive Recipe Numbers")
196 put(Recipe_ordinal, "$close-file", _CLOSE_FILE);
197 :(before "End Primitive Recipe Checks")
198 case _CLOSE_FILE: {
199 if (SIZE(inst.ingredients) != 1) {
200 raise << maybe(get(Recipe, r).name) << "'$close-file' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
201 break;
203 if (!is_mu_number(inst.ingredients.at(0))) {
204 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();
205 break;
207 if (SIZE(inst.products) != 1) {
208 raise << maybe(get(Recipe, r).name) << "'$close-file' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end();
209 break;
211 if (inst.products.at(0).name != inst.ingredients.at(0).name) {
212 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();
213 break;
215 break;
217 :(before "End Primitive Recipe Implementations")
218 case _CLOSE_FILE: {
219 long long int x = static_cast<long long int>(ingredients.at(0).at(0));
220 FILE* f = reinterpret_cast<FILE*>(x);
221 fclose(f);
222 products.resize(1);
223 products.at(0).push_back(0); // todo: ensure that caller always resets the ingredient
224 break;