MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode_imp.cpp
Go to the documentation of this file.
1
6#include "icode.hpp"
7#include <algorithm>
8
9namespace pascal {
10
16 static std::string toLower(const std::string &s) {
17 std::string r = s;
18 std::transform(r.begin(), r.end(), r.begin(),
19 [](unsigned char c) { return std::tolower(c); });
20 return r;
21 }
22
23 std::unordered_set<std::string> sdlFunctions = {
24 "sdl_init", "sdl_quit", "sdl_create_window", "sdl_destroy_window", "sdl_set_window_title",
25 "sdl_set_window_position", "sdl_get_window_size", "sdl_set_window_fullscreen", "sdl_set_window_icon",
26 "sdl_create_renderer", "sdl_destroy_renderer", "sdl_set_draw_color", "sdl_clear", "sdl_present",
27 "sdl_get_renderer_output_size", "sdl_draw_point", "sdl_draw_line", "sdl_draw_rect", "sdl_fill_rect",
28 "sdl_poll_event", "sdl_get_event_type", "sdl_get_key_code", "sdl_get_mouse_x", "sdl_get_mouse_y",
29 "sdl_get_mouse_button", "sdl_get_mouse_buttons", "sdl_get_relative_mouse_x", "sdl_get_relative_mouse_y",
30 "sdl_get_relative_mouse_buttons", "sdl_create_rgb_surface", "sdl_free_surface", "sdl_blit_surface",
31 "sdl_get_mouse_state", "sdl_get_relative_mouse_state", "sdl_get_keyboard_state", "sdl_is_key_pressed",
32 "sdl_get_num_keys", "sdl_set_clipboard_text", "sdl_get_clipboard_text", "sdl_show_cursor",
33 "sdl_create_texture", "sdl_destroy_texture", "sdl_load_texture", "sdl_load_texture_color_key", "sdl_load_texture_color_key_rgb", "sdl_render_texture",
34 "sdl_update_texture", "sdl_lock_texture", "sdl_unlock_texture", "sdl_get_ticks", "sdl_delay",
35 "sdl_open_audio", "sdl_close_audio", "sdl_pause_audio", "sdl_load_wav", "sdl_free_wav",
36 "sdl_queue_audio", "sdl_get_queued_audio_size", "sdl_clear_queued_audio", "sdl_init_text",
37 "sdl_quit_text", "sdl_load_font", "sdl_draw_text", "sdl_create_render_target", "sdl_set_render_target",
38 "sdl_destroy_render_target", "sdl_present_scaled", "sdl_present_stretched"};
39
40 std::unordered_set<std::string> stdFunctions = {
41 "argc", "argv", "set_program_args", "free_program_args", "abs", "fabs",
42 "sqrt", "pow", "sin", "cos", "tan", "floor", "ceil", "rand", "srand",
43 "malloc", "calloc", "free", "toupper", "tolower", "isalpha", "isdigit",
44 "isspace", "atoi", "atof", "exit", "system", "memcpy", "memcmp",
45 "memmove", "memset", "exp", "exp2", "log", "log10", "log2", "fmod",
46 "atan2", "asin", "acos", "atan", "sinh", "cosh", "tanh", "hypot",
47 "round", "trunc", "float_to_int", "int_to_float", "halt",
48 "ord", "chr", "succ", "pred", "inc", "dec", "include", "exclude"};
49
50 bool IOFunctionHandler::canHandle(const std::string &funcName) const {
51 auto f = toLower(funcName);
52 return f == "writeln" || f == "write" || f == "readln" ||
53 f == "seed_random" || f == "rand_number";
54 }
55
56 void IOFunctionHandler::generate(CodeGenVisitor &visitor, const std::string &funcName_,
57 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
58 auto funcName = toLower(funcName_);
59 if (funcName == "writeln" || funcName == "write") {
60 // Check if first argument is a file variable
61 bool fileOutput = false;
62 std::string fileSlot;
63 size_t startArg = 0;
64 if (!arguments.empty()) {
65 if (auto *varNode = dynamic_cast<VariableNode *>(arguments[0].get())) {
66 std::string mangledName = visitor.findMangledName(varNode->name);
67 if (visitor.fileVars.count(toLower(mangledName)) ||
68 visitor.fileVars.count(toLower(varNode->name))) {
69 fileOutput = true;
70 fileSlot = visitor.eval(arguments[0].get());
71 startArg = 1;
72 visitor.usedModules.insert("io");
73 }
74 }
75 }
76
77 for (size_t i = startArg; i < arguments.size(); ++i) {
78 const auto &arg = arguments[i];
79 std::string val = visitor.eval(arg.get());
80 VarType type = visitor.getExpressionType(arg.get());
81
82 if (fileOutput) {
83 // Use fprintf for file output
84 if (type == VarType::STRING || type == VarType::PTR) {
85 visitor.usedStrings.insert("fmt_str");
86 visitor.emit_invoke("fprintf", {fileSlot, "fmt_str", val});
87 } else if (type == VarType::DOUBLE) {
88 visitor.usedStrings.insert("fmt_float");
89 visitor.emit_invoke("fprintf", {fileSlot, "fmt_float", val});
90 } else if (type == VarType::CHAR) {
91 visitor.usedStrings.insert("fmt_chr");
92 visitor.emit_invoke("fprintf", {fileSlot, "fmt_chr", val});
93 } else {
94 visitor.usedStrings.insert("fmt_int");
95 visitor.emit_invoke("fprintf", {fileSlot, "fmt_int", val});
96 }
97 } else {
98 if (type == VarType::STRING || type == VarType::PTR) {
99 visitor.usedStrings.insert("fmt_str");
100 visitor.emit2("print", "fmt_str", val);
101 } else if (type == VarType::DOUBLE) {
102 visitor.usedStrings.insert("fmt_float");
103 visitor.emit2("print", "fmt_float", val);
104 } else if (type == VarType::CHAR) {
105 visitor.usedStrings.insert("fmt_chr");
106 visitor.emit2("print", "fmt_chr", val);
107 } else {
108 visitor.usedStrings.insert("fmt_int");
109 visitor.emit2("print", "fmt_int", val);
110 }
111 }
112 if (visitor.isReg(val) && !visitor.isParmReg(val)) {
113 visitor.freeReg(val);
114 }
115 }
116 if (funcName == "writeln") {
117 if (fileOutput) {
118 std::string nlLabel = visitor.internString("\n");
119 visitor.emit_invoke("fputs", {nlLabel, fileSlot});
120 } else {
121 visitor.usedStrings.insert("newline");
122 visitor.emit1("print", "newline");
123 }
124 }
125 if (fileOutput && visitor.isReg(fileSlot) && !visitor.isParmReg(fileSlot))
126 visitor.freeReg(fileSlot);
127 } else if (funcName == "readln") {
128 if (arguments.empty()) {
129 visitor.emit1("getline", "input_buffer");
130 return;
131 }
132
133 // Check if first argument is a file variable
134 bool fileInput = false;
135 std::string fileSlot;
136 size_t startArg = 0;
137 if (auto *firstVar = dynamic_cast<VariableNode *>(arguments[0].get())) {
138 std::string mangledName = visitor.findMangledName(firstVar->name);
139 if (visitor.fileVars.count(toLower(mangledName)) ||
140 visitor.fileVars.count(toLower(firstVar->name))) {
141 fileInput = true;
142 fileSlot = visitor.eval(arguments[0].get());
143 startArg = 1;
144 visitor.usedModules.insert("io");
145 }
146 }
147
148 for (size_t i = startArg; i < arguments.size(); ++i) {
149 auto &arg = arguments[i];
150 if (auto varNode = dynamic_cast<VariableNode *>(arg.get())) {
151 std::string varName = varNode->name;
152 int slot = visitor.newSlotFor(varName);
153 std::string memLoc = visitor.slotVar(slot);
154
155 auto varType = visitor.getVarType(varName);
156
157 if (fileInput) {
158 // Read line from file using mxvm_fgets
159 visitor.emit_invoke("mxvm_fgets", {fileSlot});
160 std::string retReg = visitor.allocReg();
161 visitor.emit("return " + retReg);
162 if (varType == VarType::DOUBLE) {
163 visitor.emit2("to_float", memLoc, retReg);
164 } else if (varType == VarType::INT) {
165 visitor.emit2("to_int", memLoc, retReg);
166 } else {
167 visitor.emit2("mov", memLoc, retReg);
168 }
169 visitor.freeReg(retReg);
170 } else {
171 visitor.emit1("getline", "input_buffer");
172 if (varType == VarType::DOUBLE) {
173 visitor.emit2("to_float", memLoc, "input_buffer");
174 } else if (varType == VarType::INT) {
175 visitor.emit2("to_int", memLoc, "input_buffer");
176 }
177 }
178
179 visitor.recordLocation(varName, {CodeGenVisitor::ValueLocation::MEMORY, memLoc});
180 } else {
181 int lineNum = arg->getLineNumber();
182 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
183 ": readln argument must be a variable");
184 }
185 }
186 if (fileInput && visitor.isReg(fileSlot) && !visitor.isParmReg(fileSlot))
187 visitor.freeReg(fileSlot);
188 } else if (funcName == "seed_random") {
189 visitor.usedModules.insert("io");
190 if (arguments.size() != 0)
191 throw std::runtime_error("seed_random requires 0 arguments");
192 visitor.emit_invoke("seed_random", {});
193 }
194 }
195
196 bool IOFunctionHandler::generateWithResult(CodeGenVisitor &visitor, const std::string &funcName_,
197 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
198 auto funcName = toLower(funcName_);
199 if (funcName == "seed_random") {
200 visitor.usedModules.insert("io");
201 if (arguments.size() != 0)
202 throw std::runtime_error("seed_random requires 0 arguments");
203 std::string dst = visitor.allocReg();
204 visitor.emit_invoke("seed_random", {});
205 visitor.emit("return " + dst);
206 visitor.pushValue(dst);
207 return true;
208 } else if (funcName == "rand_number") {
209 visitor.usedModules.insert("io");
210 if (arguments.size() != 1)
211 throw std::runtime_error("rand_number requires 1 argument (size)");
212 std::string arg = visitor.eval(arguments[0].get());
213 std::string dst = visitor.allocReg();
214 visitor.emit_invoke("rand_number", {arg});
215 visitor.emit("return " + dst);
216 if (visitor.isReg(arg) && !visitor.isParmReg(arg))
217 visitor.freeReg(arg);
218 visitor.pushValue(dst);
219 return true;
220 }
221 return false;
222 }
223
224 bool StdFunctionHandler::canHandle(const std::string &funcName) const {
225 return stdFunctions.find(toLower(funcName)) != stdFunctions.end();
226 }
227
228 void StdFunctionHandler::generate(CodeGenVisitor &visitor, const std::string &funcName,
229 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
230 visitor.usedModules.insert("std");
231
232 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
233
234 std::vector<std::string> args;
235 for (auto &arg : arguments) {
236 args.push_back(visitor.eval(arg.get()));
237 }
238
239 if (funcName == "srand") {
240 if (arguments.size() != 1)
241 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
242 ": srand requires 1 argument");
243 visitor.emit_invoke("srand", args);
244 } else if (funcName == "free") {
245 if (arguments.size() != 1)
246 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
247 ": free requires 1 argument");
248 visitor.emit_invoke("release", args);
249 } else if (funcName == "halt") {
250 if (arguments.size() != 1)
251 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
252 ": halt requires 1 argument");
253 visitor.emit("exit " + args[0]);
254 } else if (funcName == "inc") {
255 if (arguments.size() < 1 || arguments.size() > 2)
256 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
257 ": inc requires 1 or 2 arguments");
258 std::string amount = (arguments.size() == 2) ? args[1] : "1";
259 visitor.emit2("add", args[0], amount);
260 } else if (funcName == "dec") {
261 if (arguments.size() < 1 || arguments.size() > 2)
262 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
263 ": dec requires 1 or 2 arguments");
264 std::string amount = (arguments.size() == 2) ? args[1] : "1";
265 visitor.emit2("sub", args[0], amount);
266 } else if (funcName == "include") {
267 if (arguments.size() != 2)
268 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
269 ": include requires 2 arguments (set, element)");
270 std::string setPtr = visitor.ensurePtrBase(args[0]);
271 std::string oneReg = visitor.allocReg();
272 visitor.emit2("mov", oneReg, "1");
273 visitor.emit4("store", oneReg, setPtr, args[1], "8");
274 visitor.freeReg(oneReg);
275 } else if (funcName == "exclude") {
276 if (arguments.size() != 2)
277 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
278 ": exclude requires 2 arguments (set, element)");
279 std::string setPtr = visitor.ensurePtrBase(args[0]);
280 std::string zeroReg = visitor.allocReg();
281 visitor.emit2("mov", zeroReg, "0");
282 visitor.emit4("store", zeroReg, setPtr, args[1], "8");
283 visitor.freeReg(zeroReg);
284 }
285
286 for (const std::string &arg : args) {
287 if (visitor.isReg(arg))
288 visitor.freeReg(arg);
289 }
290 }
291
292 bool StdFunctionHandler::generateWithResult(CodeGenVisitor &visitor, const std::string &funcName,
293 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
294 visitor.usedModules.insert("std");
295
296 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
297
298 std::unordered_set<std::string> floatMathFuncs = {
299 "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh",
300 "exp", "log", "log10", "sqrt", "pow", "fmod", "ceil", "floor", "fabs",
301 "round", "trunc", "hypot"};
302
303 if (floatMathFuncs.find(funcName) != floatMathFuncs.end()) {
304 int requiredArgs = (funcName == "pow" || funcName == "fmod" || funcName == "hypot") ? 2 : 1;
305 if (static_cast<int>(arguments.size()) != requiredArgs)
306 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
307 ": " + funcName + " requires " + std::to_string(requiredArgs) + " argument(s)");
308
309 std::vector<std::string> args;
310 args.reserve(arguments.size());
311
312 for (const auto &argNode : arguments) {
313 std::string argLocation = visitor.eval(argNode.get());
314 auto argType = visitor.getExpressionType(argNode.get());
315
316 if (argType != VarType::DOUBLE) {
317 std::string floatReg = visitor.allocFloatReg();
318 visitor.emit2("to_float", floatReg, argLocation);
319 if (visitor.isReg(argLocation))
320 visitor.freeReg(argLocation);
321 args.push_back(floatReg);
322 } else {
323 if (!visitor.isFloatReg(argLocation)) {
324 std::string floatReg = visitor.allocFloatReg();
325 visitor.emit2("mov", floatReg, argLocation);
326 args.push_back(floatReg);
327 } else {
328 args.push_back(argLocation);
329 }
330 }
331 }
332
333 visitor.emit_invoke(funcName, args);
334 std::string resultReg = visitor.allocFloatReg();
335 visitor.emit("return " + resultReg);
336 visitor.pushValue(resultReg);
337 for (const std::string &fr : args) {
338 if (visitor.isFloatReg(fr)) {
339 visitor.freeFloatReg(fr);
340 }
341 }
342 return true;
343 } else if (funcName == "abs") {
344 if (arguments.size() != 1)
345 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
346 ": abs requires 1 argument");
347 std::string a = visitor.eval(arguments[0].get());
348
349 std::string resultReg = visitor.allocReg();
350 visitor.emit_invoke("abs", {a});
351 visitor.emit("return " + resultReg);
352 visitor.pushValue(resultReg);
353 if (visitor.isReg(a))
354 visitor.freeReg(a);
355 return true;
356 } else if (funcName == "rand") {
357 if (arguments.size() != 0)
358 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
359 ": rand requires 0 arguments");
360 visitor.emit_invoke("rand", {});
361 std::string resultReg = visitor.allocReg();
362 visitor.emit("return " + resultReg);
363 visitor.pushValue(resultReg);
364 return true;
365 } else if (funcName == "float" || funcName == "real") {
366 if (arguments.size() != 1) {
367 throw std::runtime_error("float/real expects one argument");
368 }
369 std::string argLocation = visitor.eval(arguments[0].get());
370
371 auto argType = visitor.getExpressionType(arguments[0].get());
372 if (argType == VarType::DOUBLE) {
373 visitor.pushValue(argLocation);
374 return true;
375 }
376
377 std::string floatReg = visitor.allocFloatReg();
378 visitor.emit2("to_float", floatReg, argLocation);
379 visitor.pushValue(floatReg);
380 if (visitor.isReg(argLocation))
381 visitor.freeReg(argLocation);
382 return true;
383 } else if (funcName == "ord") {
384 if (arguments.size() != 1)
385 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
386 ": ord requires 1 argument");
387 std::string a = visitor.eval(arguments[0].get());
388 // ord() is identity for integers/chars/enums — value is already an ordinal
389 visitor.pushValue(a);
390 return true;
391 } else if (funcName == "chr") {
392 if (arguments.size() != 1)
393 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
394 ": chr requires 1 argument");
395 std::string a = visitor.eval(arguments[0].get());
396 // chr() is identity — just changes interpretation to char
397 visitor.pushValue(a);
398 return true;
399 } else if (funcName == "succ") {
400 if (arguments.size() != 1)
401 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
402 ": succ requires 1 argument");
403 std::string a = visitor.eval(arguments[0].get());
404 std::string r = visitor.allocReg();
405 visitor.emit2("mov", r, a);
406 visitor.emit2("add", r, "1");
407 if (visitor.isReg(a) && !visitor.isParmReg(a))
408 visitor.freeReg(a);
409 visitor.pushValue(r);
410 return true;
411 } else if (funcName == "pred") {
412 if (arguments.size() != 1)
413 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
414 ": pred requires 1 argument");
415 std::string a = visitor.eval(arguments[0].get());
416 std::string r = visitor.allocReg();
417 visitor.emit2("mov", r, a);
418 visitor.emit2("sub", r, "1");
419 if (visitor.isReg(a) && !visitor.isParmReg(a))
420 visitor.freeReg(a);
421 visitor.pushValue(r);
422 return true;
423 }
424
425 return false;
426 }
427
428 bool SDLFunctionHandler::canHandle(const std::string &funcName) const {
429 return sdlFunctions.count(toLower(funcName)) != 0;
430 }
431
432 void SDLFunctionHandler::generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) {
433 visitor.usedModules.insert("sdl");
434
435 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
436
437 std::vector<std::string> args;
438 args.reserve(arguments.size());
439 for (auto &arg : arguments)
440 args.push_back(visitor.eval(arg.get()));
441
442 auto freeArgs = [&]() {
443 for (const std::string &a : args)
444 if (visitor.isReg(a))
445 visitor.freeReg(a);
446 };
447
448 if (funcName == "sdl_init") {
449 if (arguments.size() != 0)
450 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
451 ": sdl_init requires 0 arguments");
452 visitor.emit_invoke("init", {});
453 } else if (funcName == "sdl_quit") {
454 if (arguments.size() != 0)
455 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
456 ": sdl_quit requires 0 arguments");
457 visitor.emit_invoke("quit", {});
458 } else if (funcName == "sdl_destroy_window") {
459 if (arguments.size() != 1)
460 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
461 ": sdl_destroy_window requires 1 argument (window_id)");
462 visitor.emit_invoke("destroy_window", args);
463 } else if (funcName == "sdl_destroy_renderer") {
464 if (arguments.size() != 1)
465 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
466 ": sdl_destroy_renderer requires 1 argument (renderer_id)");
467 visitor.emit_invoke("destroy_renderer", args);
468 } else if (funcName == "sdl_set_window_title") {
469 if (arguments.size() != 2)
470 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
471 ": sdl_set_window_title requires 2 arguments (window_id, title)");
472 visitor.emit_invoke("set_window_title", args);
473 } else if (funcName == "sdl_set_window_position") {
474 if (arguments.size() != 3)
475 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
476 ": sdl_set_window_position requires 3 arguments (window_id, x, y)");
477 visitor.emit_invoke("set_window_position", args);
478 } else if (funcName == "sdl_get_window_size") {
479 if (arguments.size() != 3)
480 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
481 ": sdl_get_window_size requires 3 arguments (window_id, w_var, h_var)");
482 visitor.emit_invoke("get_window_size", args);
483 } else if (funcName == "sdl_set_window_fullscreen") {
484 if (arguments.size() != 2)
485 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
486 ": sdl_set_window_fullscreen requires 2 arguments (window_id, fullscreen)");
487 visitor.emit_invoke("set_window_fullscreen", args);
488 } else if (funcName == "sdl_set_window_icon") {
489 if (arguments.size() != 2)
490 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
491 ": sdl_set_window_icon requires 2 arguments (window_id, path)");
492 visitor.emit_invoke("set_window_icon", args);
493 } else if (funcName == "sdl_set_draw_color") {
494 if (arguments.size() != 5)
495 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
496 ": sdl_set_draw_color requires 5 arguments (renderer_id, r, g, b, a)");
497 visitor.emit_invoke("set_draw_color", args);
498 } else if (funcName == "sdl_clear") {
499 if (arguments.size() != 1)
500 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
501 ": sdl_clear requires 1 argument (renderer_id)");
502 visitor.emit_invoke("clear", args);
503 } else if (funcName == "sdl_present") {
504 if (arguments.size() != 1)
505 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
506 ": sdl_present requires 1 argument (renderer_id)");
507 visitor.emit_invoke("present", args);
508 } else if (funcName == "sdl_draw_point") {
509 if (arguments.size() != 3)
510 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
511 ": sdl_draw_point requires 3 arguments (renderer_id, x, y)");
512 visitor.emit_invoke("draw_point", args);
513 } else if (funcName == "sdl_draw_line") {
514 if (arguments.size() != 5)
515 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
516 ": sdl_draw_line requires 5 arguments (renderer_id, x1, y1, x2, y2)");
517 visitor.emit_invoke("draw_line", args);
518 } else if (funcName == "sdl_draw_rect") {
519 if (arguments.size() != 5)
520 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
521 ": sdl_draw_rect requires 5 arguments (renderer_id, x, y, w, h)");
522 visitor.emit_invoke("draw_rect", args);
523 } else if (funcName == "sdl_fill_rect") {
524 if (arguments.size() != 5)
525 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
526 ": sdl_fill_rect requires 5 arguments (renderer_id, x, y, w, h)");
527 visitor.emit_invoke("fill_rect", args);
528 } else if (funcName == "sdl_get_mouse_state") {
529 if (arguments.size() != 2)
530 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
531 ": sdl_get_mouse_state requires 2 arguments (x_var, y_var)");
532 visitor.emit_invoke("get_mouse_state", args);
533 } else if (funcName == "sdl_get_relative_mouse_state") {
534 if (arguments.size() != 2)
535 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
536 ": sdl_get_relative_mouse_state requires 2 arguments (x_var, y_var)");
537 visitor.emit_invoke("get_relative_mouse_state", args);
538 } else if (funcName == "sdl_get_keyboard_state") {
539 if (arguments.size() != 1)
540 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
541 ": sdl_get_keyboard_state requires 1 argument (numkeys_var)");
542 visitor.emit_invoke("get_keyboard_state", args);
543 } else if (funcName == "sdl_show_cursor") {
544 if (arguments.size() != 1)
545 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
546 ": sdl_show_cursor requires 1 argument (show)");
547 visitor.emit_invoke("show_cursor", args);
548 } else if (funcName == "sdl_free_surface") {
549 if (arguments.size() != 1)
550 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
551 ": sdl_free_surface requires 1 argument (surf_ptr)");
552 visitor.emit_invoke("free_surface", args);
553 } else if (funcName == "sdl_set_clipboard_text") {
554 if (arguments.size() != 1)
555 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
556 ": sdl_set_clipboard_text requires 1 argument (text)");
557 visitor.emit_invoke("set_clipboard_text", args);
558 } else if (funcName == "sdl_destroy_texture") {
559 if (arguments.size() != 1)
560 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
561 ": sdl_destroy_texture requires 1 argument (texture_id)");
562 visitor.emit_invoke("destroy_texture", args);
563 } else if (funcName == "sdl_render_texture") {
564 if (arguments.size() != 10)
565 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
566 ": sdl_render_texture requires 10 arguments (renderer_id, texture_id, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h)");
567 visitor.emit_invoke("render_texture", args);
568 } else if (funcName == "sdl_delay") {
569 if (arguments.size() != 1)
570 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
571 ": sdl_delay requires 1 argument (ms)");
572 visitor.emit_invoke("delay", args);
573 } else if (funcName == "sdl_close_audio") {
574 if (arguments.size() != 0)
575 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
576 ": sdl_close_audio requires 0 arguments");
577 visitor.emit_invoke("close_audio", {});
578 } else if (funcName == "sdl_pause_audio") {
579 if (arguments.size() != 1)
580 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
581 ": sdl_pause_audio requires 1 argument (pause_on)");
582 visitor.emit_invoke("pause_audio", args);
583 } else if (funcName == "sdl_free_wav") {
584 if (arguments.size() != 1)
585 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
586 ": sdl_free_wav requires 1 argument (audio_buf)");
587 visitor.emit_invoke("free_wav", args);
588 } else if (funcName == "sdl_queue_audio") {
589 if (arguments.size() != 2)
590 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
591 ": sdl_queue_audio requires 2 arguments (data, len)");
592 visitor.emit_invoke("queue_audio", args);
593 } else if (funcName == "sdl_clear_queued_audio") {
594 if (arguments.size() != 0)
595 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
596 ": sdl_clear_queued_audio requires 0 arguments");
597 visitor.emit_invoke("clear_queued_audio", {});
598 } else if (funcName == "sdl_update_texture") {
599 if (arguments.size() != 3)
600 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
601 ": sdl_update_texture requires 3 arguments (texture_id, pixels, pitch)");
602 visitor.emit_invoke("update_texture", args);
603 } else if (funcName == "sdl_unlock_texture") {
604 if (arguments.size() != 1)
605 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
606 ": sdl_unlock_texture requires 1 argument (texture_id)");
607 visitor.emit_invoke("unlock_texture", args);
608 } else if (funcName == "sdl_init_text") {
609 if (arguments.size() != 0)
610 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
611 ": sdl_init_text requires 0 arguments");
612 visitor.emit_invoke("init_text", {});
613 } else if (funcName == "sdl_quit_text") {
614 if (arguments.size() != 0)
615 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
616 ": sdl_quit_text requires 0 arguments");
617 visitor.emit_invoke("quit_text", {});
618 } else if (funcName == "sdl_draw_text") {
619 if (arguments.size() != 9)
620 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
621 ": sdl_draw_text requires 9 arguments (renderer_id, font_id, text, x, y, r, g, b, a)");
622 visitor.emit_invoke("draw_text", args);
623 } else if (funcName == "sdl_set_render_target") {
624 if (arguments.size() != 2)
625 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
626 ": sdl_set_render_target requires 2 arguments (renderer_id, target_id)");
627 visitor.emit_invoke("set_render_target", args);
628 } else if (funcName == "sdl_destroy_render_target") {
629 if (arguments.size() != 1)
630 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
631 ": sdl_destroy_render_target requires 1 argument (target_id)");
632 visitor.emit_invoke("destroy_render_target", args);
633 } else if (funcName == "sdl_present_scaled") {
634 if (arguments.size() != 4)
635 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
636 ": sdl_present_scaled requires 4 arguments (renderer_id, target_id, src_width, src_height)");
637 visitor.emit_invoke("present_scaled", args);
638 } else if (funcName == "sdl_present_stretched") {
639 if (arguments.size() != 4)
640 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
641 ": sdl_present_stretched requires 4 arguments (renderer_id, target_id, dst_width, dst_height)");
642 visitor.emit_invoke("present_stretched", args);
643 } else if (funcName == "sdl_lock_texture") {
644 if (arguments.size() != 3)
645 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
646 ": sdl_lock_texture requires 3 arguments (texture_id, pixels_var, pitch_var)");
647 visitor.emit_invoke("lock_texture", args);
648 }
649
650 freeArgs();
651 }
652
653 bool SDLFunctionHandler::generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) {
654 visitor.usedModules.insert("sdl");
655
656 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
657
658 std::vector<std::string> args;
659 args.reserve(arguments.size());
660 for (auto &arg : arguments)
661 args.push_back(visitor.eval(arg.get()));
662
663 auto freeArgs = [&]() {
664 for (const std::string &a : args)
665 if (visitor.isReg(a))
666 visitor.freeReg(a);
667 };
668
669 auto emitCallWithReturn = [&](const std::string &name) -> std::string {
670 std::string dst = visitor.allocReg();
671 visitor.emit_invoke(name, args);
672 visitor.emit("return " + dst);
673 return dst;
674 };
675
676 if (funcName == "sdl_init") {
677 if (arguments.size() != 0)
678 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
679 ": sdl_init requires 0 arguments");
680 std::string r = emitCallWithReturn("init");
681 visitor.pushValue(r);
682 return true;
683 } else if (funcName == "sdl_create_window") {
684 if (arguments.size() != 6)
685 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
686 ": sdl_create_window requires 6 arguments (title, x, y, w, h, flags)");
687 std::string r = emitCallWithReturn("create_window");
688 visitor.pushValue(r);
689 freeArgs();
690 return true;
691 } else if (funcName == "sdl_create_renderer") {
692 if (arguments.size() != 3)
693 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
694 ": sdl_create_renderer requires 3 arguments (window_id, index, flags)");
695 std::string r = emitCallWithReturn("create_renderer");
696 visitor.pushValue(r);
697 freeArgs();
698 return true;
699 } else if (funcName == "sdl_poll_event") {
700 if (arguments.size() != 0)
701 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
702 ": sdl_poll_event requires 0 arguments");
703 std::string r = emitCallWithReturn("poll_event");
704 visitor.pushValue(r);
705 return true;
706 } else if (funcName == "sdl_get_event_type") {
707 if (arguments.size() != 0)
708 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
709 ": sdl_get_event_type requires 0 arguments");
710 std::string r = emitCallWithReturn("get_event_type");
711 visitor.pushValue(r);
712 return true;
713 } else if (funcName == "sdl_get_key_code") {
714 if (arguments.size() != 0)
715 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
716 ": sdl_get_key_code requires 0 arguments");
717 std::string r = emitCallWithReturn("get_key_code");
718 visitor.pushValue(r);
719 return true;
720 } else if (funcName == "sdl_get_mouse_x") {
721 if (arguments.size() != 0)
722 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
723 ": sdl_get_mouse_x requires 0 arguments");
724 std::string r = emitCallWithReturn("get_mouse_x");
725 visitor.pushValue(r);
726 return true;
727 } else if (funcName == "sdl_get_mouse_y") {
728 if (arguments.size() != 0)
729 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
730 ": sdl_get_mouse_y requires 0 arguments");
731 std::string r = emitCallWithReturn("get_mouse_y");
732 visitor.pushValue(r);
733 return true;
734 } else if (funcName == "sdl_get_mouse_button") {
735 if (arguments.size() != 0)
736 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
737 ": sdl_get_mouse_button requires 0 arguments");
738 std::string r = emitCallWithReturn("get_mouse_button");
739 visitor.pushValue(r);
740 return true;
741 } else if (funcName == "sdl_get_mouse_buttons") {
742 if (arguments.size() != 0)
743 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
744 ": sdl_get_mouse_buttons requires 0 arguments");
745 std::string r = emitCallWithReturn("get_mouse_buttons");
746 visitor.pushValue(r);
747 return true;
748 } else if (funcName == "sdl_get_relative_mouse_x") {
749 if (arguments.size() != 0)
750 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
751 ": sdl_get_relative_mouse_x requires 0 arguments");
752 std::string r = emitCallWithReturn("get_relative_mouse_x");
753 visitor.pushValue(r);
754 return true;
755 } else if (funcName == "sdl_get_relative_mouse_y") {
756 if (arguments.size() != 0)
757 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
758 ": sdl_get_relative_mouse_y requires 0 arguments");
759 std::string r = emitCallWithReturn("get_relative_mouse_y");
760 visitor.pushValue(r);
761 return true;
762 } else if (funcName == "sdl_get_relative_mouse_buttons") {
763 if (arguments.size() != 0)
764 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
765 ": sdl_get_relative_mouse_buttons requires 0 arguments");
766 std::string r = emitCallWithReturn("get_relative_mouse_buttons");
767 visitor.pushValue(r);
768 return true;
769 } else if (funcName == "sdl_is_key_pressed") {
770 if (arguments.size() != 1)
771 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
772 ": sdl_is_key_pressed requires 1 argument (scancode)");
773 std::string r = emitCallWithReturn("is_key_pressed");
774 visitor.pushValue(r);
775 freeArgs();
776 return true;
777 } else if (funcName == "sdl_get_num_keys") {
778 if (arguments.size() != 0)
779 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
780 ": sdl_get_num_keys requires 0 arguments");
781 std::string r = emitCallWithReturn("get_num_keys");
782 visitor.pushValue(r);
783 return true;
784 } else if (funcName == "sdl_get_clipboard_text") {
785 if (arguments.size() != 0)
786 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
787 ": sdl_get_clipboard_text requires 0 arguments");
788 std::string r = emitCallWithReturn("get_clipboard_text");
789 visitor.pushValue(r);
790 return true;
791 } else if (funcName == "sdl_create_rgb_surface") {
792 if (arguments.size() != 3)
793 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
794 ": sdl_create_rgb_surface requires 3 arguments (width, height, depth)");
795 std::string r = emitCallWithReturn("create_rgb_surface");
796 visitor.pushValue(r);
797 freeArgs();
798 return true;
799 } else if (funcName == "sdl_blit_surface") {
800 if (arguments.size() != 4)
801 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
802 ": sdl_blit_surface requires 4 arguments (src_ptr, dst_ptr, x, y)");
803 std::string r = emitCallWithReturn("blit_surface");
804 visitor.pushValue(r);
805 freeArgs();
806 return true;
807 } else if (funcName == "sdl_create_texture") {
808 if (arguments.size() != 5)
809 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
810 ": sdl_create_texture requires 5 arguments (renderer_id, format, access, w, h)");
811 std::string r = emitCallWithReturn("create_texture");
812 visitor.pushValue(r);
813 freeArgs();
814 return true;
815 } else if (funcName == "sdl_load_texture") {
816 if (arguments.size() != 2)
817 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
818 ": sdl_load_texture requires 2 arguments (renderer_id, file_path)");
819 std::string r = emitCallWithReturn("load_texture");
820 visitor.pushValue(r);
821 freeArgs();
822 return true;
823 } else if (funcName == "sdl_load_texture_color_key") {
824 if (arguments.size() != 2)
825 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
826 ": sdl_load_texture_color_key requires 2 arguments (renderer_id, file_path)");
827 std::string r = emitCallWithReturn("load_texture_color_key");
828 visitor.pushValue(r);
829 freeArgs();
830 return true;
831 } else if (funcName == "sdl_load_texture_color_key_rgb") {
832 if (arguments.size() != 5)
833 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
834 ": sdl_load_texture_color_key_rgb requires 5 arguments (renderer_id, file_path, r, g, b)");
835 std::string r = emitCallWithReturn("load_texture_color_key_rgb");
836 visitor.pushValue(r);
837 freeArgs();
838 return true;
839 } else if (funcName == "sdl_get_ticks") {
840 if (arguments.size() != 0)
841 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
842 ": sdl_get_ticks requires 0 arguments");
843 std::string r = emitCallWithReturn("get_ticks");
844 visitor.pushValue(r);
845 return true;
846 } else if (funcName == "sdl_open_audio") {
847 if (arguments.size() != 4)
848 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
849 ": sdl_open_audio requires 4 arguments (freq, format, channels, samples)");
850 std::string r = emitCallWithReturn("open_audio");
851 visitor.pushValue(r);
852 freeArgs();
853 return true;
854 } else if (funcName == "sdl_load_wav") {
855 if (arguments.size() != 4)
856 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
857 ": sdl_load_wav requires 4 arguments (file_path, audio_buf_var, audio_len_var, audio_spec_var)");
858 std::string r = emitCallWithReturn("load_wav");
859 visitor.pushValue(r);
860 freeArgs();
861 return true;
862 } else if (funcName == "sdl_get_queued_audio_size") {
863 if (arguments.size() != 0)
864 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
865 ": sdl_get_queued_audio_size requires 0 arguments");
866 std::string r = emitCallWithReturn("get_queued_audio_size");
867 visitor.pushValue(r);
868 return true;
869 } else if (funcName == "sdl_init_text") {
870 if (arguments.size() != 0)
871 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
872 ": sdl_init_text requires 0 arguments");
873 std::string r = emitCallWithReturn("init_text");
874 visitor.pushValue(r);
875 return true;
876 } else if (funcName == "sdl_load_font") {
877 if (arguments.size() != 2)
878 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
879 ": sdl_load_font requires 2 arguments (file, ptsize)");
880 std::string r = emitCallWithReturn("load_font");
881 visitor.pushValue(r);
882 freeArgs();
883 return true;
884 } else if (funcName == "sdl_create_render_target") {
885 if (arguments.size() != 3)
886 throw std::runtime_error("Error on line " + std::to_string(lineNum) +
887 ": sdl_create_render_target requires 3 arguments (renderer_id, width, height)");
888 std::string r = emitCallWithReturn("create_render_target");
889 visitor.pushValue(r);
890 freeArgs();
891 return true;
892 }
893
894 return false;
895 }
896
897 bool StringFunctionHandler::canHandle(const std::string &f) const {
898 static const std::unordered_set<std::string> funcs = {
899 "length", "pos", "copy", "insert", "delete", "inttostr", "strtoint"};
900 return funcs.count(toLower(f)) != 0;
901 }
902
903 VarType StringFunctionHandler::getReturnType(const std::string &f_) const {
904 auto f = toLower(f_);
905 if (f == "length" || f == "pos" || f == "strtoint")
906 return VarType::INT;
907 if (f == "copy" || f == "insert" || f == "delete" || f == "inttostr")
908 return VarType::PTR;
909 return VarType::UNKNOWN;
910 }
911
913 const std::string &f,
914 const std::vector<std::unique_ptr<ASTNode>> &args) {
915 generateWithResult(v, f, args);
916 }
917
919 const std::string &f,
920 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
921
922 v.usedModules.insert("string");
923 std::vector<std::string> a;
924 a.reserve(arguments.size());
925 for (auto &n : arguments)
926 a.push_back(v.eval(n.get()));
927
928 auto freeArgs = [&]() {
929 for (auto &x : a)
930 if (v.isReg(x) && !v.isParmReg(x))
931 v.freeReg(x);
932 };
933
934 auto emitIntRet = [&](const std::string &name) {
935 v.emit_invoke(name, a);
936 std::string r = v.allocReg();
937 v.emit("return " + r);
938 v.pushValue(r);
939 };
940 auto emitPtrRet = [&](const std::string &name) {
941 v.emit_invoke(name, a);
942 std::string r = v.allocTempPtr();
943 v.emit("return " + r);
944 v.pushValue(r);
945 v.markAllocatedPtr(r);
946 };
947
948 if (f == "length") {
949 if (a.size() != 1)
950 throw std::runtime_error("length expects 1 arg");
951 emitIntRet("strlen");
952 } else if (f == "pos") {
953 if (a.size() != 2)
954 throw std::runtime_error("pos expects 2 args");
955 emitIntRet("strfind");
956 } else if (f == "strtoint") {
957 if (a.size() != 1)
958 throw std::runtime_error("strtoint expects 1 arg");
959 emitIntRet("strtoint");
960 } else if (f == "inttostr") {
961 if (a.size() != 1)
962 throw std::runtime_error("inttostr expects 1 arg");
963 emitPtrRet("inttostr");
964 } else if (f == "copy") {
965 if (a.size() != 3)
966 throw std::runtime_error("copy expects 3 args");
967 emitPtrRet("copy");
968 } else if (f == "insert") {
969 if (a.size() != 3)
970 throw std::runtime_error("insert expects 3 args");
971 emitPtrRet("insert");
972 } else if (f == "delete") {
973 if (a.size() != 3)
974 throw std::runtime_error("delete expects 3 args");
975 emitPtrRet("delete");
976 } else {
977 freeArgs();
978 return false;
979 }
980
981 freeArgs();
982 return true;
983 }
984
985 static std::unordered_set<std::string> fileFunctions = {
986 "assign", "reset", "rewrite", "append", "close", "eof", "eoln"};
987
988 bool FileFunctionHandler::canHandle(const std::string &funcName) const {
989 return fileFunctions.find(toLower(funcName)) != fileFunctions.end();
990 }
991
992 void FileFunctionHandler::generate(CodeGenVisitor &visitor, const std::string &funcName_,
993 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
994 auto funcName = toLower(funcName_);
995 visitor.usedModules.insert("io");
996
997 if (arguments.empty())
998 throw std::runtime_error(funcName + " requires at least 1 argument");
999
1000 // Get the file variable name from the first argument
1001 auto *fileVarNode = dynamic_cast<VariableNode *>(arguments[0].get());
1002 if (!fileVarNode)
1003 throw std::runtime_error(funcName + " first argument must be a file variable");
1004
1005 std::string fileVarName = fileVarNode->name;
1006 std::string mangledFile = visitor.findMangledName(fileVarName);
1007 std::string fileSlot = visitor.eval(arguments[0].get());
1008
1009 if (funcName == "assign") {
1010 if (arguments.size() != 2)
1011 throw std::runtime_error("assign requires 2 arguments (file, filename)");
1012 std::string filenameVal = visitor.eval(arguments[1].get());
1013 // Store filename in companion variable
1014 auto fnIt = visitor.fileVarNames.find(mangledFile);
1015 if (fnIt == visitor.fileVarNames.end())
1016 fnIt = visitor.fileVarNames.find(fileVarName);
1017 if (fnIt != visitor.fileVarNames.end()) {
1018 auto slotIt = visitor.varSlot.find(fnIt->second);
1019 if (slotIt != visitor.varSlot.end())
1020 visitor.emit2("mov", visitor.slotVar(slotIt->second), filenameVal);
1021 else
1022 visitor.emit2("mov", fnIt->second, filenameVal);
1023 }
1024 if (visitor.isReg(filenameVal) && !visitor.isParmReg(filenameVal))
1025 visitor.freeReg(filenameVal);
1026 } else if (funcName == "reset" || funcName == "rewrite" || funcName == "append") {
1027 // Open file: reset='r', rewrite='w', append='a'
1028 std::string mode;
1029 if (funcName == "reset") mode = "r";
1030 else if (funcName == "rewrite") mode = "w";
1031 else mode = "a";
1032
1033 // Get filename from companion variable
1034 auto fnIt = visitor.fileVarNames.find(mangledFile);
1035 if (fnIt == visitor.fileVarNames.end())
1036 fnIt = visitor.fileVarNames.find(fileVarName);
1037 std::string filenameSlot;
1038 if (fnIt != visitor.fileVarNames.end()) {
1039 auto slotIt = visitor.varSlot.find(fnIt->second);
1040 if (slotIt != visitor.varSlot.end())
1041 filenameSlot = visitor.slotVar(slotIt->second);
1042 else
1043 filenameSlot = fnIt->second;
1044 } else {
1045 throw std::runtime_error(funcName + ": file variable not found: " + fileVarName);
1046 }
1047
1048 // Create mode string constant
1049 std::string modeLabel = visitor.internString(mode);
1050
1051 visitor.emit_invoke("fopen", {filenameSlot, modeLabel});
1052 std::string retReg = visitor.allocReg();
1053 visitor.emit("return " + retReg);
1054 // Store the file pointer back into the file variable
1055 auto fSlotIt = visitor.varSlot.find(mangledFile);
1056 if (fSlotIt != visitor.varSlot.end())
1057 visitor.emit2("mov", visitor.slotVar(fSlotIt->second), retReg);
1058 else
1059 visitor.emit2("mov", mangledFile, retReg);
1060 visitor.freeReg(retReg);
1061 } else if (funcName == "close") {
1062 visitor.emit_invoke("fclose", {fileSlot});
1063 }
1064
1065 if (visitor.isReg(fileSlot) && !visitor.isParmReg(fileSlot))
1066 visitor.freeReg(fileSlot);
1067 }
1068
1069 bool FileFunctionHandler::generateWithResult(CodeGenVisitor &visitor, const std::string &funcName_,
1070 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
1071 auto funcName = toLower(funcName_);
1072 visitor.usedModules.insert("io");
1073
1074 if (funcName == "eof") {
1075 if (arguments.size() != 1)
1076 throw std::runtime_error("eof requires 1 argument (file variable)");
1077 std::string fileSlot = visitor.eval(arguments[0].get());
1078 visitor.emit_invoke("feof", {fileSlot});
1079 std::string dst = visitor.allocReg();
1080 visitor.emit("return " + dst);
1081 if (visitor.isReg(fileSlot) && !visitor.isParmReg(fileSlot))
1082 visitor.freeReg(fileSlot);
1083 visitor.pushValue(dst);
1084 return true;
1085 } else if (funcName == "eoln") {
1086 // eoln not fully supported, return false as a stub
1087 if (arguments.size() != 1)
1088 throw std::runtime_error("eoln requires 1 argument (file variable)");
1089 std::string dst = visitor.allocReg();
1090 visitor.emit2("mov", dst, "0");
1091 visitor.pushValue(dst);
1092 return true;
1093 }
1094 return false;
1095 }
1096
1097 VarType FileFunctionHandler::getReturnType(const std::string &funcName) const {
1098 auto f = toLower(funcName);
1099 if (f == "eof" || f == "eoln")
1100 return VarType::INT;
1101 return VarType::UNKNOWN;
1102 }
1103
1104} // namespace pascal
AST visitor that generates MXVM intermediate code.
Definition icode.hpp:178
void freeReg(const std::string &reg)
Definition icode.hpp:439
void pushValue(const std::string &v)
Definition icode.hpp:621
bool isParmReg(const std::string &name) const
Definition icode.hpp:775
std::string internString(const std::string &val)
Definition icode.hpp:578
std::unordered_map< std::string, std::string > fileVarNames
Maps file variable name to companion filename variable name.
Definition icode.hpp:1500
std::string allocFloatReg()
Definition icode.hpp:412
std::string allocReg()
Definition icode.hpp:389
std::string ensurePtrBase(const std::string &v)
Definition icode.hpp:2171
void emit(const std::string &s)
Definition icode.hpp:525
void emit2(const std::string &op, const std::string &a, const std::string &b)
Definition icode.hpp:530
std::string allocTempPtr(const std::string &forScope="")
Definition icode.hpp:502
void emit1(const std::string &op, const std::string &a)
Definition icode.hpp:529
bool isFloatReg(const std::string &name) const
Definition icode.hpp:455
VarType getExpressionType(ASTNode *node)
Definition icode.hpp:1614
void freeFloatReg(const std::string &reg)
Definition icode.hpp:448
VarType getVarType(const std::string &name) const
Definition icode.hpp:276
std::unordered_map< std::string, int > varSlot
Definition icode.hpp:340
std::unordered_set< std::string > fileVars
Set of variable names that are file types (pointer to FILE).
Definition icode.hpp:1498
void emit4(const std::string &op, const std::string &a, const std::string &b, const std::string &c, const std::string &d)
Definition icode.hpp:543
std::string findMangledName(const std::string &name) const
Definition icode.hpp:1855
void emit_invoke(const std::string &funcName, const std::vector< std::string > &params)
Emit an invoke instruction calling a named function with parameters.
Definition icode.hpp:1287
std::unordered_set< std::string > usedStrings
Definition icode.hpp:345
std::string eval(ASTNode *n)
Definition icode.hpp:634
bool isReg(const std::string &name) const
Definition icode.hpp:457
int newSlotFor(const std::string &name)
Definition icode.hpp:461
void markAllocatedPtr(const std::string &p)
Mark a pointer name as allocated (will be freed at scope end).
Definition icode.hpp:1280
std::string slotVar(int slot) const
Definition icode.hpp:372
std::set< std::string > usedModules
Definition icode.hpp:184
void recordLocation(const std::string &var, ValueLocation loc)
Definition icode.hpp:647
VarType getReturnType(const std::string &funcName) const override
Return the VarType produced by funcName.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
Definition icode_imp.cpp:56
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
Definition icode_imp.cpp:50
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
VarType getReturnType(const std::string &funcName) const override
Return the VarType produced by funcName.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a variable reference.
Definition ast.hpp:513
Definition ast.cpp:9
static std::unordered_set< std::string > fileFunctions
std::unordered_set< std::string > stdFunctions
Definition icode_imp.cpp:40
std::unordered_set< std::string > sdlFunctions
Definition icode_imp.cpp:23
VarType
Enumeration of variable / expression value types.
Definition icode.hpp:72
@ CHAR
character value
Definition icode.hpp:76
@ INT
integer
Definition icode.hpp:73
@ STRING
string value
Definition icode.hpp:75
@ UNKNOWN
unresolved type
Definition icode.hpp:80
@ PTR
generic pointer
Definition icode.hpp:78
@ DOUBLE
real (floating-point)
Definition icode.hpp:74
static std::string toLower(const std::string &s)
Convert a string to lowercase for case-insensitive comparison.
Definition icode_imp.cpp:16
Pascal-to-MXVM code generator using the Visitor pattern over the Pascal AST.
@ MEMORY
value is in a memory slot
Definition icode.hpp:361