16 static std::string
toLower(
const std::string &s) {
18 std::transform(r.begin(), r.end(), r.begin(),
19 [](
unsigned char c) { return std::tolower(c); });
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"};
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"};
52 return f ==
"writeln" || f ==
"write" || f ==
"readln" ||
53 f ==
"seed_random" || f ==
"rand_number";
57 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
58 auto funcName =
toLower(funcName_);
59 if (funcName ==
"writeln" || funcName ==
"write") {
61 bool fileOutput =
false;
64 if (!arguments.empty()) {
65 if (
auto *varNode =
dynamic_cast<VariableNode *
>(arguments[0].get())) {
70 fileSlot = visitor.
eval(arguments[0].get());
77 for (
size_t i = startArg; i < arguments.size(); ++i) {
78 const auto &arg = arguments[i];
79 std::string val = visitor.
eval(arg.get());
86 visitor.
emit_invoke(
"fprintf", {fileSlot,
"fmt_str", val});
89 visitor.
emit_invoke(
"fprintf", {fileSlot,
"fmt_float", val});
92 visitor.
emit_invoke(
"fprintf", {fileSlot,
"fmt_chr", val});
95 visitor.
emit_invoke(
"fprintf", {fileSlot,
"fmt_int", val});
100 visitor.
emit2(
"print",
"fmt_str", val);
103 visitor.
emit2(
"print",
"fmt_float", val);
106 visitor.
emit2(
"print",
"fmt_chr", val);
109 visitor.
emit2(
"print",
"fmt_int", val);
116 if (funcName ==
"writeln") {
122 visitor.
emit1(
"print",
"newline");
125 if (fileOutput && visitor.
isReg(fileSlot) && !visitor.
isParmReg(fileSlot))
127 }
else if (funcName ==
"readln") {
128 if (arguments.empty()) {
129 visitor.
emit1(
"getline",
"input_buffer");
134 bool fileInput =
false;
135 std::string fileSlot;
137 if (
auto *firstVar =
dynamic_cast<VariableNode *
>(arguments[0].get())) {
142 fileSlot = visitor.
eval(arguments[0].get());
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;
153 std::string memLoc = visitor.
slotVar(slot);
160 std::string retReg = visitor.
allocReg();
161 visitor.
emit(
"return " + retReg);
163 visitor.
emit2(
"to_float", memLoc, retReg);
165 visitor.
emit2(
"to_int", memLoc, retReg);
167 visitor.
emit2(
"mov", memLoc, retReg);
171 visitor.
emit1(
"getline",
"input_buffer");
173 visitor.
emit2(
"to_float", memLoc,
"input_buffer");
175 visitor.
emit2(
"to_int", memLoc,
"input_buffer");
181 int lineNum = arg->getLineNumber();
182 throw std::runtime_error(
"Error on line " + std::to_string(lineNum) +
183 ": readln argument must be a variable");
186 if (fileInput && visitor.
isReg(fileSlot) && !visitor.
isParmReg(fileSlot))
188 }
else if (funcName ==
"seed_random") {
190 if (arguments.size() != 0)
191 throw std::runtime_error(
"seed_random requires 0 arguments");
197 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
198 auto funcName =
toLower(funcName_);
199 if (funcName ==
"seed_random") {
201 if (arguments.size() != 0)
202 throw std::runtime_error(
"seed_random requires 0 arguments");
203 std::string dst = visitor.
allocReg();
205 visitor.
emit(
"return " + dst);
208 }
else if (funcName ==
"rand_number") {
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();
215 visitor.
emit(
"return " + dst);
229 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
232 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
234 std::vector<std::string> args;
235 for (
auto &arg : arguments) {
236 args.push_back(visitor.
eval(arg.get()));
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");
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");
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)");
271 std::string oneReg = visitor.
allocReg();
272 visitor.
emit2(
"mov", oneReg,
"1");
273 visitor.
emit4(
"store", oneReg, setPtr, args[1],
"8");
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)");
280 std::string zeroReg = visitor.
allocReg();
281 visitor.
emit2(
"mov", zeroReg,
"0");
282 visitor.
emit4(
"store", zeroReg, setPtr, args[1],
"8");
286 for (
const std::string &arg : args) {
287 if (visitor.
isReg(arg))
293 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
296 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
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"};
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)");
309 std::vector<std::string> args;
310 args.reserve(arguments.size());
312 for (
const auto &argNode : arguments) {
313 std::string argLocation = visitor.
eval(argNode.get());
318 visitor.
emit2(
"to_float", floatReg, argLocation);
319 if (visitor.
isReg(argLocation))
321 args.push_back(floatReg);
325 visitor.
emit2(
"mov", floatReg, argLocation);
326 args.push_back(floatReg);
328 args.push_back(argLocation);
335 visitor.
emit(
"return " + resultReg);
337 for (
const std::string &fr : args) {
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());
349 std::string resultReg = visitor.
allocReg();
351 visitor.
emit(
"return " + resultReg);
353 if (visitor.
isReg(a))
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");
361 std::string resultReg = visitor.
allocReg();
362 visitor.
emit(
"return " + resultReg);
365 }
else if (funcName ==
"float" || funcName ==
"real") {
366 if (arguments.size() != 1) {
367 throw std::runtime_error(
"float/real expects one argument");
369 std::string argLocation = visitor.
eval(arguments[0].get());
378 visitor.
emit2(
"to_float", floatReg, argLocation);
380 if (visitor.
isReg(argLocation))
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());
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());
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());
405 visitor.
emit2(
"mov", r, a);
406 visitor.
emit2(
"add", r,
"1");
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());
417 visitor.
emit2(
"mov", r, a);
418 visitor.
emit2(
"sub", r,
"1");
435 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
437 std::vector<std::string> args;
438 args.reserve(arguments.size());
439 for (
auto &arg : arguments)
440 args.push_back(visitor.
eval(arg.get()));
442 auto freeArgs = [&]() {
443 for (
const std::string &a : args)
444 if (visitor.
isReg(a))
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");
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");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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");
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)");
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)");
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)");
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");
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)");
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)");
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");
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");
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)");
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)");
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)");
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)");
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)");
656 int lineNum = arguments.empty() ? 1 : arguments[0]->getLineNumber();
658 std::vector<std::string> args;
659 args.reserve(arguments.size());
660 for (
auto &arg : arguments)
661 args.push_back(visitor.
eval(arg.get()));
663 auto freeArgs = [&]() {
664 for (
const std::string &a : args)
665 if (visitor.
isReg(a))
669 auto emitCallWithReturn = [&](
const std::string &name) -> std::string {
670 std::string dst = visitor.
allocReg();
672 visitor.
emit(
"return " + dst);
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
898 static const std::unordered_set<std::string> funcs = {
899 "length",
"pos",
"copy",
"insert",
"delete",
"inttostr",
"strtoint"};
900 return funcs.count(
toLower(f)) != 0;
905 if (f ==
"length" || f ==
"pos" || f ==
"strtoint")
907 if (f ==
"copy" || f ==
"insert" || f ==
"delete" || f ==
"inttostr")
913 const std::string &f,
914 const std::vector<std::unique_ptr<ASTNode>> &args) {
919 const std::string &f,
920 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
923 std::vector<std::string> a;
924 a.reserve(arguments.size());
925 for (
auto &n : arguments)
926 a.push_back(v.
eval(n.get()));
928 auto freeArgs = [&]() {
934 auto emitIntRet = [&](
const std::string &name) {
937 v.
emit(
"return " + r);
940 auto emitPtrRet = [&](
const std::string &name) {
943 v.
emit(
"return " + r);
950 throw std::runtime_error(
"length expects 1 arg");
951 emitIntRet(
"strlen");
952 }
else if (f ==
"pos") {
954 throw std::runtime_error(
"pos expects 2 args");
955 emitIntRet(
"strfind");
956 }
else if (f ==
"strtoint") {
958 throw std::runtime_error(
"strtoint expects 1 arg");
959 emitIntRet(
"strtoint");
960 }
else if (f ==
"inttostr") {
962 throw std::runtime_error(
"inttostr expects 1 arg");
963 emitPtrRet(
"inttostr");
964 }
else if (f ==
"copy") {
966 throw std::runtime_error(
"copy expects 3 args");
968 }
else if (f ==
"insert") {
970 throw std::runtime_error(
"insert expects 3 args");
971 emitPtrRet(
"insert");
972 }
else if (f ==
"delete") {
974 throw std::runtime_error(
"delete expects 3 args");
975 emitPtrRet(
"delete");
986 "assign",
"reset",
"rewrite",
"append",
"close",
"eof",
"eoln"};
993 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
994 auto funcName =
toLower(funcName_);
997 if (arguments.empty())
998 throw std::runtime_error(funcName +
" requires at least 1 argument");
1001 auto *fileVarNode =
dynamic_cast<VariableNode *
>(arguments[0].get());
1003 throw std::runtime_error(funcName +
" first argument must be a file variable");
1005 std::string fileVarName = fileVarNode->name;
1007 std::string fileSlot = visitor.
eval(arguments[0].get());
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());
1018 auto slotIt = visitor.
varSlot.find(fnIt->second);
1019 if (slotIt != visitor.
varSlot.end())
1020 visitor.
emit2(
"mov", visitor.
slotVar(slotIt->second), filenameVal);
1022 visitor.
emit2(
"mov", fnIt->second, filenameVal);
1024 if (visitor.
isReg(filenameVal) && !visitor.
isParmReg(filenameVal))
1026 }
else if (funcName ==
"reset" || funcName ==
"rewrite" || funcName ==
"append") {
1029 if (funcName ==
"reset") mode =
"r";
1030 else if (funcName ==
"rewrite") mode =
"w";
1037 std::string filenameSlot;
1039 auto slotIt = visitor.
varSlot.find(fnIt->second);
1040 if (slotIt != visitor.
varSlot.end())
1041 filenameSlot = visitor.
slotVar(slotIt->second);
1043 filenameSlot = fnIt->second;
1045 throw std::runtime_error(funcName +
": file variable not found: " + fileVarName);
1051 visitor.
emit_invoke(
"fopen", {filenameSlot, modeLabel});
1052 std::string retReg = visitor.
allocReg();
1053 visitor.
emit(
"return " + retReg);
1055 auto fSlotIt = visitor.
varSlot.find(mangledFile);
1056 if (fSlotIt != visitor.
varSlot.end())
1057 visitor.
emit2(
"mov", visitor.
slotVar(fSlotIt->second), retReg);
1059 visitor.
emit2(
"mov", mangledFile, retReg);
1061 }
else if (funcName ==
"close") {
1070 const std::vector<std::unique_ptr<ASTNode>> &arguments) {
1071 auto funcName =
toLower(funcName_);
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());
1079 std::string dst = visitor.
allocReg();
1080 visitor.
emit(
"return " + dst);
1085 }
else if (funcName ==
"eoln") {
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");
1099 if (f ==
"eof" || f ==
"eoln")
AST visitor that generates MXVM intermediate code.
void freeReg(const std::string ®)
void pushValue(const std::string &v)
bool isParmReg(const std::string &name) const
std::string internString(const std::string &val)
std::unordered_map< std::string, std::string > fileVarNames
Maps file variable name to companion filename variable name.
std::string allocFloatReg()
std::string ensurePtrBase(const std::string &v)
void emit(const std::string &s)
void emit2(const std::string &op, const std::string &a, const std::string &b)
std::string allocTempPtr(const std::string &forScope="")
void emit1(const std::string &op, const std::string &a)
bool isFloatReg(const std::string &name) const
VarType getExpressionType(ASTNode *node)
void freeFloatReg(const std::string ®)
VarType getVarType(const std::string &name) const
std::unordered_map< std::string, int > varSlot
std::unordered_set< std::string > fileVars
Set of variable names that are file types (pointer to FILE).
void emit4(const std::string &op, const std::string &a, const std::string &b, const std::string &c, const std::string &d)
std::string findMangledName(const std::string &name) const
void emit_invoke(const std::string &funcName, const std::vector< std::string > ¶ms)
Emit an invoke instruction calling a named function with parameters.
std::unordered_set< std::string > usedStrings
std::string eval(ASTNode *n)
bool isReg(const std::string &name) const
int newSlotFor(const std::string &name)
void markAllocatedPtr(const std::string &p)
Mark a pointer name as allocated (will be freed at scope end).
std::string slotVar(int slot) const
std::set< std::string > usedModules
void recordLocation(const std::string &var, ValueLocation loc)
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).
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.
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.
static std::unordered_set< std::string > fileFunctions
std::unordered_set< std::string > stdFunctions
std::unordered_set< std::string > sdlFunctions
VarType
Enumeration of variable / expression value types.
@ DOUBLE
real (floating-point)
static std::string toLower(const std::string &s)
Convert a string to lowercase for case-insensitive comparison.
Pascal-to-MXVM code generator using the Visitor pattern over the Pascal AST.
@ MEMORY
value is in a memory slot