Line data Source code
1 : /* command.cpp: script binding and language interpretation functionality
2 : *
3 : * libprimis uses a bespoke scripting language called cubescript, which allows
4 : * for commands to be declared in the code which can be natively called upon in
5 : * games. cubescript "builtin" commands and variables are declared with macros
6 : * (see command.h) and further aliases can be defined in cubescript files.
7 : *
8 : * for the file containing the cubescript "standard library", see cubestd.cpp.
9 : * Other files contain their own relevant builtin declarations (e.g. sound vars
10 : * in sound.cpp)
11 : *
12 : * command.cpp largely handles cubescript language interpretation, through a
13 : * bytecode compiler which allows for greater speed than naive approaches; this
14 : * is mostly necessary to handle the UI system, which is built on cubescript
15 : */
16 :
17 : #include "../libprimis-headers/cube.h"
18 : #include "../../shared/stream.h"
19 :
20 : #include "console.h"
21 : #include "control.h"
22 : #include "cs.h"
23 :
24 : #include "world/octaedit.h"
25 :
26 : std::unordered_map<std::string, ident> idents; // contains ALL vars/commands/aliases
27 : static std::vector<ident *> identmap;
28 : static ident *dummyident = nullptr;
29 : std::queue<ident *> triggerqueue; //for the game to handle var change events
30 : static constexpr uint cmdqueuedepth = 128; //how many elements before oldest queued data gets discarded
31 : int identflags = 0;
32 :
33 : const char *sourcefile = nullptr,
34 : *sourcestr = nullptr;
35 :
36 : std::vector<char> strbuf[4];
37 : int stridx = 0;
38 :
39 : static constexpr int undoflag = 1<<Max_Args;
40 :
41 : static IdentLink noalias = { nullptr, nullptr, (1<<Max_Args)-1, nullptr },
42 : *aliasstack = &noalias;
43 :
44 : static int _numargs = variable("numargs", Max_Args, 0, 0, &_numargs, nullptr, 0);
45 :
46 : //ident object
47 :
48 0 : void ident::getval(tagval &r) const
49 : {
50 0 : ::getval(val, valtype, r);
51 0 : }
52 :
53 84 : void ident::getcstr(tagval &v) const
54 : {
55 84 : switch(valtype)
56 : {
57 0 : case Value_Macro:
58 : {
59 0 : v.setmacro(val.code);
60 0 : break;
61 : }
62 84 : case Value_String:
63 : case Value_CString:
64 : {
65 84 : v.setcstr(val.s);
66 84 : break;
67 : }
68 0 : case Value_Integer:
69 : {
70 0 : v.setstr(newstring(intstr(val.i)));
71 0 : break;
72 : }
73 0 : case Value_Float:
74 : {
75 0 : v.setstr(newstring(floatstr(val.f)));
76 0 : break;
77 : }
78 0 : default:
79 : {
80 0 : v.setcstr("");
81 0 : break;
82 : }
83 : }
84 84 : }
85 :
86 0 : void ident::getcval(tagval &v) const
87 : {
88 0 : switch(valtype)
89 : {
90 0 : case Value_Macro:
91 : {
92 0 : v.setmacro(val.code);
93 0 : break;
94 : }
95 0 : case Value_String:
96 : case Value_CString:
97 : {
98 0 : v.setcstr(val.s);
99 0 : break;
100 : }
101 0 : case Value_Integer:
102 : {
103 0 : v.setint(val.i);
104 0 : break;
105 : }
106 0 : case Value_Float:
107 : {
108 0 : v.setfloat(val.f);
109 0 : break;
110 : }
111 0 : default:
112 : {
113 0 : v.setnull();
114 0 : break;
115 : }
116 : }
117 0 : }
118 :
119 : //tagval object
120 :
121 2708 : void tagval::setint(int val)
122 : {
123 2708 : type = Value_Integer;
124 2708 : i = val;
125 2708 : }
126 :
127 807 : void tagval::setfloat(float val)
128 : {
129 807 : type = Value_Float;
130 807 : f = val;
131 807 : }
132 :
133 0 : void tagval::setnumber(double val)
134 : {
135 0 : i = static_cast<int>(val);
136 0 : if(val == i)
137 : {
138 0 : type = Value_Integer;
139 : }
140 : else
141 : {
142 0 : type = Value_Float;
143 0 : f = val;
144 : }
145 0 : }
146 :
147 391 : void tagval::setstr(char *val)
148 : {
149 391 : type = Value_String;
150 391 : s = val;
151 391 : }
152 :
153 2407 : void tagval::setnull()
154 : {
155 2407 : type = Value_Null;
156 2407 : i = 0;
157 2407 : }
158 :
159 289 : void tagval::setcode(const uint *val)
160 : {
161 289 : type = Value_Code;
162 289 : code = val;
163 289 : }
164 :
165 627 : void tagval::setmacro(const uint *val)
166 : {
167 627 : type = Value_Macro;
168 627 : code = val;
169 627 : }
170 :
171 84 : void tagval::setcstr(const char *val)
172 : {
173 84 : type = Value_CString;
174 84 : cstr = val;
175 84 : }
176 :
177 147 : void tagval::setident(ident *val)
178 : {
179 147 : type = Value_Ident;
180 147 : id = val;
181 147 : }
182 :
183 : //end tagval
184 :
185 1859 : static int getint(const identval &v, int type)
186 : {
187 1859 : switch(type)
188 : {
189 0 : case Value_Float:
190 : {
191 0 : return static_cast<int>(v.f);
192 : }
193 1610 : case Value_Integer:
194 : {
195 1610 : return static_cast<int>(v.i);
196 : }
197 34 : case Value_String:
198 : case Value_Macro:
199 : case Value_CString:
200 : {
201 34 : return parseint(v.s);
202 : }
203 215 : default:
204 : {
205 215 : return 0;
206 : }
207 : }
208 : }
209 :
210 1657 : int tagval::getint() const
211 : {
212 1657 : return ::getint(*this, type);
213 : }
214 :
215 202 : int ident::getint() const
216 : {
217 202 : return ::getint(val, valtype);
218 : }
219 :
220 153 : float getfloat(const identval &v, int type)
221 : {
222 153 : switch(type)
223 : {
224 126 : case Value_Float:
225 : {
226 126 : return static_cast<float>(v.f);
227 : }
228 0 : case Value_Integer:
229 : {
230 0 : return static_cast<float>(v.i);
231 : }
232 25 : case Value_String:
233 : case Value_Macro:
234 : case Value_CString:
235 : {
236 25 : return parsefloat(v.s);
237 : }
238 2 : default:
239 : {
240 2 : return 0.f;
241 : }
242 : }
243 : }
244 :
245 131 : float tagval::getfloat() const
246 : {
247 131 : return ::getfloat(*this, type);
248 : }
249 :
250 22 : float ident::getfloat() const
251 : {
252 22 : return ::getfloat(val, valtype);
253 : }
254 :
255 0 : static double getnumber(const identval &v, int type)
256 : {
257 0 : switch(type)
258 : {
259 0 : case Value_Float:
260 : {
261 0 : return static_cast<double>(v.f);
262 : }
263 0 : case Value_Integer:
264 : {
265 0 : return static_cast<double>(v.i);
266 : }
267 0 : case Value_String:
268 : case Value_Macro:
269 : case Value_CString:
270 : {
271 0 : return parsenumber(v.s);
272 : }
273 0 : default:
274 : {
275 0 : return 0.0;
276 : }
277 : }
278 : }
279 :
280 0 : double tagval::getnumber() const
281 : {
282 0 : return ::getnumber(*this, type);
283 : }
284 :
285 0 : double ident::getnumber() const
286 : {
287 0 : return ::getnumber(val, valtype);
288 : }
289 :
290 5938 : void freearg(tagval &v)
291 : {
292 5938 : switch(v.type)
293 : {
294 147 : case Value_String:
295 : {
296 147 : delete[] v.s;
297 147 : break;
298 : }
299 289 : case Value_Code:
300 : {
301 289 : if(v.code[-1] == Code_Start)
302 : {
303 : //delete starting at index **[-1]**, since tagvals get passed arrays starting at index 1
304 0 : delete[] &v.code[-1];
305 : }
306 289 : break;
307 : }
308 : }
309 5938 : }
310 :
311 1363 : static void forcenull(tagval &v)
312 : {
313 1363 : switch(v.type)
314 : {
315 1363 : case Value_Null:
316 : {
317 1363 : return;
318 : }
319 : }
320 0 : freearg(v);
321 0 : v.setnull();
322 : }
323 :
324 0 : static float forcefloat(tagval &v)
325 : {
326 0 : float f = 0.0f;
327 0 : switch(v.type)
328 : {
329 0 : case Value_Integer:
330 : {
331 0 : f = v.i;
332 0 : break;
333 : }
334 0 : case Value_String:
335 : case Value_Macro:
336 : case Value_CString:
337 : {
338 0 : f = parsefloat(v.s);
339 0 : break;
340 : }
341 0 : case Value_Float:
342 : {
343 0 : return v.f;
344 : }
345 : }
346 0 : freearg(v);
347 0 : v.setfloat(f);
348 0 : return f;
349 : }
350 :
351 1050 : static int forceint(tagval &v)
352 : {
353 1050 : int i = 0;
354 1050 : switch(v.type)
355 : {
356 40 : case Value_Float:
357 : {
358 40 : i = v.f;
359 40 : break;
360 : }
361 66 : case Value_String:
362 : case Value_Macro:
363 : case Value_CString:
364 : {
365 66 : i = parseint(v.s);
366 66 : break;
367 : }
368 0 : case Value_Integer:
369 : {
370 0 : return v.i;
371 : }
372 : }
373 1050 : freearg(v);
374 1050 : v.setint(i);
375 1050 : return i;
376 : }
377 :
378 0 : static const char *forcestr(tagval &v)
379 : {
380 0 : const char *s = "";
381 0 : switch(v.type)
382 : {
383 0 : case Value_Float:
384 : {
385 0 : s = floatstr(v.f);
386 0 : break;
387 : }
388 0 : case Value_Integer:
389 : {
390 0 : s = intstr(v.i);
391 0 : break;
392 : }
393 0 : case Value_Macro:
394 : case Value_CString:
395 : {
396 0 : s = v.s;
397 0 : break;
398 : }
399 0 : case Value_String:
400 : {
401 0 : return v.s;
402 : }
403 : }
404 0 : freearg(v);
405 0 : v.setstr(newstring(s));
406 0 : return s;
407 : }
408 :
409 2799 : static void forcearg(tagval &v, int type)
410 : {
411 2799 : switch(type)
412 : {
413 3 : case Ret_String:
414 : {
415 3 : if(v.type != Value_String)
416 : {
417 0 : forcestr(v);
418 : }
419 3 : break;
420 : }
421 1432 : case Ret_Integer:
422 : {
423 1432 : if(v.type != Value_Integer)
424 : {
425 1050 : forceint(v);
426 : }
427 1432 : break;
428 : }
429 0 : case Ret_Float:
430 : {
431 0 : if(v.type != Value_Float)
432 : {
433 0 : forcefloat(v);
434 : }
435 0 : break;
436 : }
437 : }
438 2799 : }
439 :
440 0 : void tagval::cleanup()
441 : {
442 0 : freearg(*this);
443 0 : }
444 :
445 1341 : static void freeargs(tagval *args, int &oldnum, int newnum)
446 : {
447 4233 : for(int i = newnum; i < oldnum; i++)
448 : {
449 2892 : freearg(args[i]);
450 : }
451 1341 : oldnum = newnum;
452 1341 : }
453 :
454 555 : void cleancode(ident &id)
455 : {
456 555 : if(id.code)
457 : {
458 0 : id.code[0] -= 0x100;
459 0 : if(static_cast<int>(id.code[0]) < 0x100)
460 : {
461 0 : delete[] id.code;
462 : }
463 0 : id.code = nullptr;
464 : }
465 555 : }
466 :
467 : static tagval noret = NullVal();
468 :
469 : tagval * commandret = &noret;
470 :
471 1 : void clear_command()
472 : {
473 1109 : for(auto& [k, i] : idents)
474 : {
475 1108 : if(i.type==Id_Alias)
476 : {
477 28 : delete[] i.name;
478 28 : i.name = nullptr;
479 :
480 28 : i.forcenull();
481 :
482 28 : delete[] i.code;
483 28 : i.code = nullptr;
484 : }
485 : }
486 1 : }
487 :
488 1 : void clearoverride(ident &i)
489 : {
490 1 : if(!(i.flags&Idf_Overridden))
491 : {
492 1 : return;
493 : }
494 0 : switch(i.type)
495 : {
496 0 : case Id_Alias:
497 : {
498 0 : if(i.valtype==Value_String)
499 : {
500 0 : if(!i.val.s[0])
501 : {
502 0 : break;
503 : }
504 0 : delete[] i.val.s;
505 : }
506 0 : cleancode(i);
507 0 : i.valtype = Value_String;
508 0 : i.val.s = newstring("");
509 0 : break;
510 : }
511 0 : case Id_Var:
512 : {
513 0 : *i.storage.i = i.overrideval.i;
514 0 : i.changed();
515 0 : break;
516 : }
517 0 : case Id_FloatVar:
518 : {
519 0 : *i.storage.f = i.overrideval.f;
520 0 : i.changed();
521 0 : break;
522 : }
523 0 : case Id_StringVar:
524 : {
525 0 : delete[] *i.storage.s;
526 0 : *i.storage.s = i.overrideval.s;
527 0 : i.changed();
528 0 : break;
529 : }
530 : }
531 0 : i.flags &= ~Idf_Overridden;
532 : }
533 :
534 0 : void clearoverrides()
535 : {
536 0 : for(auto& [k, id] : idents)
537 : {
538 0 : clearoverride(id);
539 : }
540 0 : }
541 :
542 : static bool initedidents = false;
543 : static std::vector<ident> *identinits = nullptr;
544 :
545 1789 : static ident *addident(const ident &id)
546 : {
547 1789 : if(!initedidents)
548 : {
549 655 : if(!identinits)
550 : {
551 1 : identinits = new std::vector<ident>;
552 : }
553 655 : identinits->push_back(id);
554 655 : return nullptr;
555 : }
556 1134 : const auto itr = idents.find(id.name);
557 1134 : if(itr == idents.end())
558 : {
559 : //we need to make a new entry
560 1114 : idents[id.name] = id;
561 : }
562 1134 : ident &def = idents[id.name];
563 1134 : def.index = identmap.size();
564 1134 : identmap.push_back(&def);
565 1134 : return identmap.back();
566 : }
567 :
568 : ident *newident(const char *name, int flags = 0);
569 :
570 1 : bool initidents()
571 : {
572 1 : initedidents = true;
573 26 : for(int i = 0; i < Max_Args; i++)
574 : {
575 50 : std::string argname = std::string("arg").append(std::to_string(i+1));
576 25 : newident(argname.c_str(), Idf_Arg);
577 25 : }
578 1 : dummyident = newident("//dummy", Idf_Unknown);
579 1 : if(identinits)
580 : {
581 656 : for(uint i = 0; i < (*identinits).size(); i++)
582 : {
583 655 : addident((*identinits)[i]);
584 : }
585 1 : if(identinits)
586 : {
587 1 : delete identinits;
588 1 : identinits = nullptr;
589 : }
590 : }
591 1 : return true;
592 : }
593 :
594 0 : static const char *debugline(const char *p, const char *fmt)
595 : {
596 0 : if(!sourcestr)
597 : {
598 0 : return fmt;
599 : }
600 0 : int num = 1;
601 0 : const char *line = sourcestr;
602 : for(;;)
603 : {
604 0 : const char *end = std::strchr(line, '\n'); //search for newline
605 0 : if(!end)
606 : {
607 0 : end = line + std::strlen(line);
608 : }
609 0 : if(p >= line && p <= end)
610 : {
611 : static string buf;
612 0 : if(sourcefile)
613 : {
614 0 : formatstring(buf, "%s:%d: %s", sourcefile, num, fmt);
615 : }
616 : else
617 : {
618 0 : formatstring(buf, "%d: %s", num, fmt);
619 : }
620 0 : return buf;
621 : }
622 0 : if(!*end)
623 : {
624 0 : break;
625 : }
626 0 : line = end + 1;
627 0 : num++;
628 0 : }
629 0 : return fmt;
630 : }
631 :
632 : VAR(debugalias, 0, 4, 1000); //depth to which alias aliasing should be debugged (disabled if 0)
633 :
634 6 : static void dodebugalias()
635 : {
636 6 : if(!debugalias)
637 : {
638 0 : return;
639 : }
640 6 : int total = 0,
641 6 : depth = 0;
642 6 : for(IdentLink *l = aliasstack; l != &noalias; l = l->next)
643 : {
644 0 : total++;
645 : }
646 6 : for(IdentLink *l = aliasstack; l != &noalias; l = l->next)
647 : {
648 0 : ident *id = l->id;
649 0 : ++depth;
650 0 : if(depth < debugalias)
651 : {
652 0 : conoutf(Console_Error, " %d) %s", total-depth+1, id->name);
653 : }
654 0 : else if(l->next == &noalias)
655 : {
656 0 : conoutf(Console_Error, depth == debugalias ? " %d) %s" : " ..%d) %s", total-depth+1, id->name);
657 : }
658 : }
659 : }
660 :
661 : static int nodebug = 0;
662 :
663 : static void debugcode(const char *fmt, ...) PRINTFARGS(1, 2);
664 :
665 6 : static void debugcode(const char *fmt, ...)
666 : {
667 6 : if(nodebug)
668 : {
669 0 : return;
670 : }
671 : va_list args;
672 6 : va_start(args, fmt);
673 6 : conoutfv(Console_Error, fmt, args);
674 6 : va_end(args);
675 :
676 6 : dodebugalias();
677 : }
678 :
679 : static void debugcodeline(const char *p, const char *fmt, ...) PRINTFARGS(2, 3);
680 :
681 0 : static void debugcodeline(const char *p, const char *fmt, ...)
682 : {
683 0 : if(nodebug)
684 : {
685 0 : return;
686 : }
687 : va_list args;
688 0 : va_start(args, fmt);
689 0 : conoutfv(Console_Error, debugline(p, fmt), args);
690 0 : va_end(args);
691 :
692 0 : dodebugalias();
693 : }
694 :
695 95 : void pusharg(ident &id, const tagval &v, identstack &stack)
696 : {
697 95 : stack.val = id.val;
698 95 : stack.valtype = id.valtype;
699 95 : stack.next = id.stack;
700 95 : id.stack = &stack;
701 95 : id.setval(v);
702 95 : cleancode(id);
703 95 : }
704 :
705 95 : void poparg(ident &id)
706 : {
707 95 : if(!id.stack)
708 : {
709 0 : return;
710 : }
711 95 : identstack *stack = id.stack;
712 95 : if(id.valtype == Value_String)
713 : {
714 16 : delete[] id.val.s;
715 : }
716 95 : id.setval(*stack);
717 95 : cleancode(id);
718 95 : id.stack = stack->next;
719 : }
720 :
721 0 : void undoarg(ident &id, identstack &stack)
722 : {
723 0 : identstack *prev = id.stack;
724 0 : stack.val = id.val;
725 0 : stack.valtype = id.valtype;
726 0 : stack.next = prev;
727 0 : id.stack = prev->next;
728 0 : id.setval(*prev);
729 0 : cleancode(id);
730 0 : }
731 :
732 0 : void redoarg(ident &id, const identstack &stack)
733 : {
734 0 : identstack *prev = stack.next;
735 0 : prev->val = id.val;
736 0 : prev->valtype = id.valtype;
737 0 : id.stack = prev;
738 0 : id.setval(stack);
739 0 : cleancode(id);
740 0 : }
741 :
742 1 : void pushcmd(ident *id, tagval *v, const uint *code)
743 : {
744 1 : if(id->type != Id_Alias || id->index < Max_Args)
745 : {
746 0 : return;
747 : }
748 : identstack stack;
749 1 : pusharg(*id, *v, stack);
750 1 : v->type = Value_Null;
751 1 : id->flags &= ~Idf_Unknown;
752 1 : executeret(code, *commandret);
753 1 : poparg(*id);
754 : }
755 :
756 0 : static void pushalias(ident &id, identstack &stack)
757 : {
758 0 : if(id.type == Id_Alias && id.index >= Max_Args)
759 : {
760 0 : pusharg(id, NullVal(), stack);
761 0 : id.flags &= ~Idf_Unknown;
762 : }
763 0 : }
764 :
765 0 : static void popalias(ident &id)
766 : {
767 0 : if(id.type == Id_Alias && id.index >= Max_Args)
768 : {
769 0 : poparg(id);
770 : }
771 0 : }
772 :
773 : //returns whether the string passed starts with a valid number
774 : //does not check that all characters are valid, only the first number possibly succeeding a +/-/.
775 84 : static bool checknumber(const char *s)
776 : {
777 84 : if(isdigit(s[0]))
778 : {
779 50 : return true;
780 : }
781 34 : else switch(s[0])
782 : {
783 0 : case '+':
784 : case '-':
785 : {
786 0 : return isdigit(s[1]) || (s[1] == '.' && isdigit(s[2]));
787 : }
788 0 : case '.':
789 : {
790 0 : return isdigit(s[1]) != 0;
791 : }
792 34 : default:
793 : {
794 34 : return false;
795 : }
796 : }
797 : }
798 :
799 317 : ident *newident(const char *name, int flags)
800 : {
801 317 : ident *id = nullptr;
802 317 : const auto itr = idents.find(name);
803 317 : if(itr == idents.end())
804 : {
805 32 : if(checknumber(name))
806 : {
807 0 : debugcode("number %s is not a valid identifier name", name);
808 0 : return dummyident;
809 : }
810 32 : id = addident(ident(Id_Alias, newstring(name), flags));
811 : }
812 : else
813 : {
814 285 : id = &(*(itr)).second;
815 : }
816 317 : return id;
817 : }
818 :
819 0 : static ident *forceident(tagval &v)
820 : {
821 0 : switch(v.type)
822 : {
823 0 : case Value_Ident:
824 : {
825 0 : return v.id;
826 : }
827 0 : case Value_Macro:
828 : case Value_CString:
829 : {
830 0 : ident *id = newident(v.s, Idf_Unknown);
831 0 : v.setident(id);
832 0 : return id;
833 : }
834 0 : case Value_String:
835 : {
836 0 : ident *id = newident(v.s, Idf_Unknown);
837 0 : delete[] v.s;
838 0 : v.setident(id);
839 0 : return id;
840 : }
841 : }
842 0 : freearg(v);
843 0 : v.setident(dummyident);
844 0 : return dummyident;
845 : }
846 :
847 0 : ident *writeident(const char *name, int flags)
848 : {
849 0 : ident *id = newident(name, flags);
850 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
851 : {
852 0 : pusharg(*id, NullVal(), aliasstack->argstack[id->index]);
853 0 : aliasstack->usedargs |= 1<<id->index;
854 : }
855 0 : return id;
856 : }
857 :
858 1 : static void resetvar(char *name)
859 : {
860 1 : const auto itr = idents.find(name);
861 1 : if(itr == idents.end())
862 : {
863 0 : return;
864 : }
865 : else
866 : {
867 1 : ident* id = &(*(itr)).second;
868 1 : if(id->flags&Idf_ReadOnly)
869 : {
870 0 : debugcode("variable %s is read-only", id->name);
871 : }
872 : else
873 : {
874 1 : clearoverride(*id);
875 : }
876 : }
877 : }
878 :
879 0 : void setarg(ident &id, tagval &v)
880 : {
881 0 : if(aliasstack->usedargs&(1<<id.index))
882 : {
883 0 : if(id.valtype == Value_String)
884 : {
885 0 : delete[] id.val.s;
886 : }
887 0 : id.setval(v);
888 0 : cleancode(id);
889 : }
890 : else
891 : {
892 0 : pusharg(id, v, aliasstack->argstack[id.index]);
893 0 : aliasstack->usedargs |= 1<<id.index;
894 : }
895 0 : }
896 :
897 190 : void setalias(ident &id, tagval &v)
898 : {
899 190 : if(id.valtype == Value_String)
900 : {
901 80 : delete[] id.val.s;
902 : }
903 190 : id.setval(v);
904 190 : cleancode(id);
905 190 : id.flags = (id.flags & identflags) | identflags;
906 190 : }
907 :
908 3 : static void setalias(const char *name, tagval &v)
909 : {
910 3 : const auto itr = idents.find(name);
911 3 : if(itr != idents.end())
912 : {
913 1 : ident *id = &(*(itr)).second;
914 1 : switch(id->type)
915 : {
916 0 : case Id_Alias:
917 : {
918 0 : if(id->index < Max_Args)
919 : {
920 0 : setarg(*id, v);
921 : }
922 : else
923 : {
924 0 : setalias(*id, v);
925 : }
926 0 : return;
927 : }
928 0 : case Id_Var:
929 : {
930 0 : setvarchecked(id, v.getint());
931 0 : break;
932 : }
933 0 : case Id_FloatVar:
934 : {
935 0 : setfvarchecked(id, v.getfloat());
936 0 : break;
937 : }
938 1 : case Id_StringVar:
939 : {
940 1 : setsvarchecked(id, v.getstr());
941 1 : break;
942 : }
943 0 : default:
944 : {
945 0 : debugcode("cannot redefine builtin %s with an alias", id->name);
946 0 : break;
947 : }
948 : }
949 1 : freearg(v);
950 : }
951 2 : else if(checknumber(name))
952 : {
953 0 : debugcode("cannot alias number %s", name);
954 0 : freearg(v);
955 : }
956 : else
957 : {
958 2 : addident(ident(Id_Alias, newstring(name), v, identflags));
959 : }
960 : }
961 :
962 2 : void alias(const char *name, const char *str)
963 : {
964 : tagval v;
965 2 : v.setstr(newstring(str));
966 2 : setalias(name, v);
967 2 : }
968 :
969 : // variables and commands are registered through globals, see cube.h
970 :
971 387 : int variable(const char *name, int min, int cur, int max, int *storage, identfun fun, int flags)
972 : {
973 387 : addident(ident(Id_Var, name, min, max, storage, reinterpret_cast<void *>(fun), flags));
974 387 : return cur;
975 : }
976 :
977 112 : float fvariable(const char *name, float min, float cur, float max, float *storage, identfun fun, int flags)
978 : {
979 112 : addident(ident(Id_FloatVar, name, min, max, storage, reinterpret_cast<void *>(fun), flags));
980 112 : return cur;
981 : }
982 :
983 5 : char *svariable(const char *name, const char *cur, char **storage, identfun fun, int flags)
984 : {
985 5 : addident(ident(Id_StringVar, name, storage, reinterpret_cast<void *>(fun), flags));
986 5 : return newstring(cur);
987 : }
988 :
989 : struct DefVar : identval
990 : {
991 : char *name;
992 : uint *onchange;
993 :
994 1 : DefVar() : name(nullptr), onchange(nullptr) {}
995 :
996 3 : ~DefVar()
997 : {
998 3 : delete[] name;
999 3 : name = nullptr;
1000 3 : if(onchange)
1001 : {
1002 0 : freecode(onchange);
1003 : }
1004 3 : }
1005 :
1006 0 : static void changed(ident *id)
1007 : {
1008 0 : DefVar *v = static_cast<DefVar *>(id->storage.p);
1009 0 : if(v->onchange)
1010 : {
1011 0 : execute(v->onchange);
1012 : }
1013 0 : }
1014 : };
1015 :
1016 : /**
1017 : * @brief Gets the CubeScript variable.
1018 : * @param vartype the identifier, such as float, integer, var, or command.
1019 : * @param name the variable's name.
1020 : * @return ident* the pointer to the variable.
1021 : */
1022 3 : ident* getvar(int vartype, const char *name)
1023 : {
1024 3 : const auto itr = idents.find(name);
1025 3 : if(itr != idents.end())
1026 : {
1027 1 : ident *id = &(*(itr)).second;
1028 1 : if(!id || id->type!=vartype)
1029 : {
1030 1 : return nullptr;
1031 : }
1032 0 : return id;
1033 : }
1034 2 : return nullptr;
1035 : }
1036 :
1037 : /**
1038 : * @brief Overwrite an ident's array with an array comming from storage.
1039 : * @tparam T the array data type, typically StringVar.
1040 : * @param id the StringVar identifier.
1041 : * @param dst the string that will be overwritten by the `src`.
1042 : * @param src the string that will be written to `dest`.
1043 : */
1044 : template <class T>
1045 1 : void storevalarray(ident *id, T &dst, T *src) {
1046 :
1047 1 : if(identflags&Idf_Overridden || id->flags&Idf_Override)
1048 : {
1049 0 : if(id->flags&Idf_Persist)
1050 : {
1051 : // Return error.
1052 0 : debugcode("Cannot override persistent variable %s", id->name);
1053 0 : return;
1054 : }
1055 0 : if(!(id->flags&Idf_Overridden))
1056 : {
1057 : // Save source array.
1058 0 : dst = *src;
1059 0 : id->flags |= Idf_Overridden;
1060 : }
1061 : else
1062 : {
1063 : // Reset source array.
1064 0 : delete[] *src;
1065 : }
1066 : }
1067 : else
1068 : {
1069 1 : if(id->flags&Idf_Overridden)
1070 : {
1071 : // Reset saved array.
1072 0 : delete[] dst;
1073 0 : id->flags &= ~Idf_Overridden;
1074 : }
1075 : // Reset source array.
1076 1 : delete[] *src;
1077 : }
1078 : }
1079 :
1080 : /**
1081 : * @brief Overwrite an ident's value with a value comming from storage.
1082 : * @tparam T the data type, whether integer or float.
1083 : * @param id the identifier, whether integer.
1084 : * @param dst the value that will be overwritten by the `src`.
1085 : * @param src the value that will be written to `dest`.
1086 : */
1087 : template <class T>
1088 0 : void storeval(ident *id, T &dst, T *src) {
1089 :
1090 0 : if(identflags&Idf_Overridden || id->flags&Idf_Override)
1091 : {
1092 0 : if(id->flags&Idf_Persist)
1093 : {
1094 : // Return error.
1095 0 : debugcode("Cannot override persistent variable %s", id->name);
1096 0 : return;
1097 : }
1098 0 : if(!(id->flags&Idf_Overridden))
1099 : {
1100 : // Save value.
1101 0 : dst = *src;
1102 0 : id->flags |= Idf_Overridden;
1103 : }
1104 : }
1105 0 : if(id->flags&Idf_Overridden)
1106 : {
1107 : // Reset value.
1108 0 : id->flags &= ~Idf_Overridden;
1109 : }
1110 : }
1111 :
1112 0 : void setvar(const char *name, int i, bool dofunc, bool doclamp)
1113 : {
1114 0 : ident *id = getvar(Id_Var, name);
1115 0 : if(!id)
1116 : {
1117 0 : return;
1118 : }
1119 :
1120 0 : storeval(id, id->overrideval.i, id->storage.i);
1121 0 : if(doclamp)
1122 : {
1123 0 : *id->storage.i = std::clamp(i, id->minval, id->maxval);
1124 : }
1125 : else
1126 : {
1127 0 : *id->storage.i = i;
1128 : }
1129 0 : if(dofunc)
1130 : {
1131 0 : id->changed();
1132 : }
1133 : }
1134 0 : void setfvar(const char *name, float f, bool dofunc, bool doclamp)
1135 : {
1136 0 : ident *id = getvar(Id_FloatVar, name);
1137 0 : if(!id)
1138 : {
1139 0 : return;
1140 : }
1141 :
1142 0 : storeval(id, id->overrideval.f, id->storage.f);
1143 0 : if(doclamp)
1144 : {
1145 0 : *id->storage.f = std::clamp(f, id->minvalf, id->maxvalf);
1146 : }
1147 : else
1148 : {
1149 0 : *id->storage.f = f;
1150 : }
1151 0 : if(dofunc)
1152 : {
1153 0 : id->changed();
1154 : }
1155 : }
1156 0 : void setsvar(const char *name, const char *str, bool dofunc)
1157 : {
1158 0 : ident *id = getvar(Id_StringVar, name);
1159 0 : if(!id)
1160 : {
1161 0 : return;
1162 : }
1163 :
1164 0 : storevalarray(id, id->overrideval.s, id->storage.s);
1165 0 : *id->storage.s = newstring(str);
1166 0 : if(dofunc)
1167 : {
1168 0 : id->changed();
1169 : }
1170 : }
1171 0 : int getvar(const char *name)
1172 : {
1173 0 : ident *id = getvar(Id_Var, name);
1174 0 : if(!id)
1175 : {
1176 0 : return 0;
1177 : }
1178 0 : return *id->storage.i;
1179 : }
1180 1 : int getvarmin(const char *name)
1181 : {
1182 1 : ident *id = getvar(Id_Var, name);
1183 1 : if(!id)
1184 : {
1185 1 : return 0;
1186 : }
1187 0 : return id->minval;
1188 : }
1189 0 : int getvarmax(const char *name)
1190 : {
1191 0 : ident *id = getvar(Id_Var, name);
1192 0 : if(!id)
1193 : {
1194 0 : return 0;
1195 : }
1196 0 : return id->maxval;
1197 : }
1198 1 : float getfvarmin(const char *name)
1199 : {
1200 1 : ident *id = getvar(Id_FloatVar, name);
1201 1 : if(!id)
1202 : {
1203 1 : return 0;
1204 : }
1205 0 : return id->minvalf;
1206 : }
1207 1 : float getfvarmax(const char *name)
1208 : {
1209 1 : ident *id = getvar(Id_FloatVar, name);
1210 1 : if(!id)
1211 : {
1212 1 : return 0;
1213 : }
1214 0 : return id->maxvalf;
1215 : }
1216 :
1217 1 : bool identexists(const char *name)
1218 : {
1219 1 : return (idents.end() != idents.find(name));
1220 : }
1221 :
1222 0 : ident *getident(const char *name)
1223 : {
1224 0 : const auto itr = idents.find(name);
1225 0 : if(itr != idents.end())
1226 : {
1227 0 : return &(*(itr)).second;
1228 : }
1229 0 : return nullptr;
1230 : }
1231 :
1232 0 : void touchvar(const char *name)
1233 : {
1234 0 : const auto itr = idents.find(name);
1235 0 : if(itr != idents.end())
1236 : {
1237 0 : ident* id = &(*(itr)).second;
1238 0 : switch(id->type)
1239 : {
1240 0 : case Id_Var:
1241 : case Id_FloatVar:
1242 : case Id_StringVar:
1243 : {
1244 0 : id->changed();
1245 0 : break;
1246 : }
1247 : }
1248 : }
1249 0 : }
1250 :
1251 1 : const char *getalias(const char *name)
1252 : {
1253 1 : ident *i = nullptr;
1254 1 : const auto itr = idents.find(name);
1255 1 : if(itr != idents.end())
1256 : {
1257 0 : i = &(*(itr)).second;
1258 : }
1259 1 : return i && i->type==Id_Alias && (i->index >= Max_Args || aliasstack->usedargs&(1<<i->index)) ? i->getstr() : "";
1260 : }
1261 :
1262 1 : int clampvar(bool hex, std::string name, int val, int minval, int maxval)
1263 : {
1264 1 : if(val < minval)
1265 : {
1266 1 : val = minval;
1267 : }
1268 0 : else if(val > maxval)
1269 : {
1270 0 : val = maxval;
1271 : }
1272 : else
1273 : {
1274 0 : return val;
1275 : }
1276 1 : debugcode(hex ?
1277 0 : (minval <= 255 ? "valid range for %s is %d..0x%X" : "valid range for %s is 0x%X..0x%X") :
1278 : "valid range for %s is %d..%d",
1279 : name.c_str(), minval, maxval);
1280 1 : return val;
1281 : }
1282 :
1283 0 : void vartrigger(ident *id) //places an ident pointer into queue for the game to handle
1284 : {
1285 0 : triggerqueue.push(id);
1286 0 : if(triggerqueue.size() > cmdqueuedepth)
1287 : {
1288 0 : triggerqueue.pop();
1289 : }
1290 0 : }
1291 :
1292 0 : void setvarchecked(ident *id, int val)
1293 : {
1294 0 : if(id->flags&Idf_ReadOnly)
1295 : {
1296 0 : debugcode("variable %s is read-only", id->name);
1297 : }
1298 0 : else if(!(id->flags&Idf_Override) || identflags&Idf_Overridden || allowediting)
1299 : {
1300 0 : storeval(id, id->overrideval.i, id->storage.i);
1301 0 : if(val < id->minval || val > id->maxval)
1302 : {
1303 0 : val = clampvar(id->flags&Idf_Hex, std::string(id->name), val, id->minval, id->maxval);
1304 : }
1305 0 : *id->storage.i = val;
1306 0 : id->changed(); // call trigger function if available
1307 0 : if(id->flags&Idf_Override && !(identflags&Idf_Overridden))
1308 : {
1309 0 : vartrigger(id);
1310 : }
1311 : }
1312 0 : }
1313 :
1314 0 : static void setvarchecked(ident *id, tagval *args, int numargs)
1315 : {
1316 0 : int val = forceint(args[0]);
1317 0 : if(id->flags&Idf_Hex && numargs > 1)
1318 : {
1319 0 : val = (val << 16) | (forceint(args[1])<<8);
1320 0 : if(numargs > 2)
1321 : {
1322 0 : val |= forceint(args[2]);
1323 : }
1324 : }
1325 0 : setvarchecked(id, val);
1326 0 : }
1327 :
1328 0 : float clampfvar(std::string name, float val, float minval, float maxval)
1329 : {
1330 0 : if(val < minval)
1331 : {
1332 0 : val = minval;
1333 : }
1334 0 : else if(val > maxval)
1335 : {
1336 0 : val = maxval;
1337 : }
1338 : else
1339 : {
1340 0 : return val;
1341 : }
1342 0 : debugcode("valid range for %s is %s..%s", name.c_str(), floatstr(minval), floatstr(maxval));
1343 0 : return val;
1344 : }
1345 :
1346 0 : void setfvarchecked(ident *id, float val)
1347 : {
1348 0 : if(id->flags&Idf_ReadOnly)
1349 : {
1350 0 : debugcode("variable %s is read-only", id->name);
1351 : }
1352 0 : else if(!(id->flags&Idf_Override) || identflags&Idf_Overridden || allowediting)
1353 : {
1354 0 : storeval(id, id->overrideval.f, id->storage.f);
1355 0 : if(val < id->minvalf || val > id->maxvalf)
1356 : {
1357 0 : val = clampfvar(id->name, val, id->minvalf, id->maxvalf);
1358 : }
1359 0 : *id->storage.f = val;
1360 0 : id->changed();
1361 0 : if(id->flags&Idf_Override && !(identflags&Idf_Overridden))
1362 : {
1363 0 : vartrigger(id);
1364 : }
1365 : }
1366 0 : }
1367 :
1368 1 : void setsvarchecked(ident *id, const char *val)
1369 : {
1370 1 : if(id->flags&Idf_ReadOnly)
1371 : {
1372 0 : debugcode("variable %s is read-only", id->name);
1373 : }
1374 1 : else if(!(id->flags&Idf_Override) || identflags&Idf_Overridden || allowediting)
1375 : {
1376 1 : storevalarray(id, id->overrideval.s, id->storage.s);
1377 1 : *id->storage.s = newstring(val);
1378 1 : id->changed();
1379 1 : if(id->flags&Idf_Override && !(identflags&Idf_Overridden))
1380 : {
1381 0 : vartrigger(id);
1382 : }
1383 : }
1384 1 : }
1385 :
1386 596 : bool addcommand(const char *name, identfun fun, const char *args, int type)
1387 : {
1388 : /**
1389 : * @brief The argmask is of type unsigned int, but it acts as a bitmap
1390 : * corresponding to each argument passed.
1391 : *
1392 : * For parameters of type i, b, f, F, t, T, E, N, D the value is set to 0.
1393 : * For parameters named S s e r $ (symbolic values) the value is set to 1.
1394 : */
1395 596 : uint argmask = 0;
1396 :
1397 : // The number of arguments in format string.
1398 596 : int numargs = 0;
1399 596 : bool limit = true;
1400 596 : if(args)
1401 : {
1402 : /**
1403 : * @brief Parse the format string *args, and set various
1404 : * properties about the parameters of the indent. Usually up to
1405 : * Max_CommandArgs are allowed in a single command. These values must
1406 : * be set to pass to the ident::ident() constructor.
1407 : *
1408 : * Arguments are passed to the argmask bit by bit. A command that is
1409 : * "iSsiiSSi" will get an argmask of 00000000000000000000000001100110.
1410 : * Theoretically the argmask can accommodate up to a 32 parameter
1411 : * command.
1412 : *
1413 : * For example, a command called "createBoxAtCoordinates x y" with two
1414 : * parameters could be invoked by calling "createBoxAtCoordinates 2 5"
1415 : * and the argstring would be "ii".
1416 : *
1417 : * Note that boolean is actually integral-typed in cubescript. Booleans
1418 : * are an integer in functions because a function with a parameter
1419 : * string "bb" still will look like foo(int *, int *).
1420 : */
1421 :
1422 4208 : for(const char *fmt = args; *fmt; fmt++)
1423 : {
1424 3613 : switch(*fmt)
1425 : {
1426 : //normal arguments
1427 1675 : case 'i': // (int *)
1428 : case 'b': // (int *) refers to boolean
1429 : case 'f': // (float *)
1430 : case 'F':
1431 : case 't':
1432 : case 'T':
1433 : case 'E':
1434 : case 'N':
1435 : case 'D': // (int *)
1436 : {
1437 1675 : if(numargs < Max_Args)
1438 : {
1439 1675 : numargs++;
1440 : }
1441 1675 : break;
1442 : }
1443 : //special arguments: these will flip the corresponding bit in the argmask
1444 684 : case 'S':
1445 : case 's': // (char *) refers to string
1446 : case 'e': // (uint *)
1447 : case 'r':
1448 : case '$':
1449 : {
1450 684 : if(numargs < Max_Args)
1451 : {
1452 683 : argmask |= 1<<numargs;
1453 683 : numargs++;
1454 : }
1455 684 : break;
1456 : }
1457 : //these are formatting flags, they do not add to numargs
1458 1198 : case '1':
1459 : case '2':
1460 : case '3':
1461 : case '4':
1462 : {
1463 : //shift the argstring down by the value 1,2,3,4 minus the char for 0 (so int 1 2 3 4) then down one extra element
1464 1198 : if(numargs < Max_Args)
1465 : {
1466 1148 : fmt -= *fmt-'0'+1;
1467 : }
1468 1198 : break;
1469 : }
1470 : //these flags determine whether the limit flag is set, they do not add to numargs
1471 : //the limit flag limits the number of parameters to Max_CommandArgs
1472 56 : case 'C':
1473 : case 'V': // (tagval *args, int numargs)
1474 : {
1475 56 : limit = false;
1476 56 : break;
1477 : }
1478 : //kill the engine if one of the above parameter types above are not used
1479 0 : default:
1480 : {
1481 0 : fatal("builtin %s declared with illegal type: %s", name, args);
1482 0 : break;
1483 : }
1484 : }
1485 : }
1486 : }
1487 596 : if(limit && numargs > Max_CommandArgs)
1488 : {
1489 0 : fatal("builtin %s declared with too many args: %d", name, numargs);
1490 : }
1491 : //calls the ident() constructor to create a new ident object, then addident adds it to the
1492 : //global hash table
1493 596 : addident(ident(type, name, args, argmask, numargs, reinterpret_cast<void *>(fun)));
1494 596 : return false;
1495 : }
1496 :
1497 117 : const char *parsestring(const char *p)
1498 : {
1499 1442 : for(; *p; p++)
1500 : {
1501 1439 : switch(*p)
1502 : {
1503 114 : case '\r':
1504 : case '\n':
1505 : case '\"':
1506 : {
1507 114 : return p;
1508 : }
1509 0 : case '^':
1510 : {
1511 0 : if(*(++p))
1512 : {
1513 0 : break;
1514 : }
1515 0 : return p;
1516 : }
1517 : }
1518 : }
1519 3 : return p;
1520 : }
1521 :
1522 116 : int unescapestring(char *dst, const char *src, const char *end)
1523 : {
1524 116 : char *start = dst;
1525 1441 : while(src < end)
1526 : {
1527 1325 : int c = *(src++);
1528 1325 : if(c == '^')
1529 : {
1530 0 : if(src >= end)
1531 : {
1532 0 : break;
1533 : }
1534 0 : int e = *src++;
1535 0 : switch(e)
1536 : {
1537 0 : case 'n':
1538 : {
1539 0 : *dst++ = '\n';
1540 0 : break;
1541 : }
1542 0 : case 't':
1543 : {
1544 0 : *dst++ = '\t';
1545 0 : break;
1546 : }
1547 0 : case 'f':
1548 : {
1549 0 : *dst++ = '^';
1550 0 : *dst++ = 'f';
1551 0 : break;
1552 : }
1553 0 : default:
1554 : {
1555 0 : *(dst++) = e;
1556 0 : break;
1557 : }
1558 : }
1559 : }
1560 : else
1561 : {
1562 1325 : *(dst++) = c;
1563 : }
1564 : }
1565 116 : *dst = '\0';
1566 116 : return dst - start;
1567 : }
1568 :
1569 4 : static char *conc(std::vector<char> &buf, const tagval *v, int n, bool space, const char *prefix = nullptr, int prefixlen = 0)
1570 : {
1571 4 : if(prefix)
1572 : {
1573 0 : for(int i = 0; i < prefixlen; ++i)
1574 : {
1575 0 : buf.push_back(prefix[i]);
1576 : }
1577 0 : if(space && n)
1578 : {
1579 0 : buf.push_back(' ');
1580 : }
1581 : }
1582 4 : for(int i = 0; i < n; ++i)
1583 : {
1584 1 : const char *s = "";
1585 1 : int len = 0;
1586 1 : switch(v[i].type)
1587 : {
1588 0 : case Value_Integer:
1589 : {
1590 0 : s = intstr(v[i].i);
1591 0 : break;
1592 : }
1593 0 : case Value_Float:
1594 : {
1595 0 : s = floatstr(v[i].f);
1596 0 : break;
1597 : }
1598 0 : case Value_String:
1599 : case Value_CString:
1600 : {
1601 0 : s = v[i].s;
1602 0 : break;
1603 : }
1604 1 : case Value_Macro:
1605 : {
1606 1 : s = v[i].s;
1607 1 : len = v[i].code[-1]>>8;
1608 1 : goto haslen; //skip `len` assignment
1609 : }
1610 : }
1611 0 : len = static_cast<int>(std::strlen(s));
1612 1 : haslen:
1613 5 : for(int i = 0; i < len; ++i)
1614 : {
1615 4 : buf.push_back(s[i]);
1616 : }
1617 1 : if(i == n-1)
1618 : {
1619 1 : break;
1620 : }
1621 0 : if(space)
1622 : {
1623 0 : buf.push_back(' ');
1624 : }
1625 : }
1626 4 : buf.push_back('\0');
1627 4 : return buf.data();
1628 : }
1629 :
1630 27 : static char *conc(const tagval *v, int n, bool space, const char *prefix, int prefixlen)
1631 : {
1632 : static int vlen[Max_Args];
1633 : static char numbuf[3*maxstrlen];
1634 27 : int len = prefixlen,
1635 27 : numlen = 0,
1636 27 : i = 0;
1637 84 : for(; i < n; i++)
1638 : {
1639 57 : switch(v[i].type)
1640 : {
1641 45 : case Value_Macro:
1642 : {
1643 45 : len += (vlen[i] = v[i].code[-1]>>8);
1644 45 : break;
1645 : }
1646 2 : case Value_String:
1647 : case Value_CString:
1648 : {
1649 2 : len += (vlen[i] = static_cast<int>(std::strlen(v[i].s)));
1650 2 : break;
1651 : }
1652 8 : case Value_Integer:
1653 : {
1654 8 : if(numlen + maxstrlen > static_cast<int>(sizeof(numbuf)))
1655 : {
1656 0 : goto overflow;
1657 : }
1658 8 : intformat(&numbuf[numlen], v[i].i);
1659 8 : numlen += (vlen[i] = std::strlen(&numbuf[numlen]));
1660 8 : break;
1661 : }
1662 2 : case Value_Float:
1663 : {
1664 2 : if(numlen + maxstrlen > static_cast<int>(sizeof(numbuf)))
1665 : {
1666 0 : goto overflow;
1667 : }
1668 2 : floatformat(&numbuf[numlen], v[i].f);
1669 2 : numlen += (vlen[i] = std::strlen(&numbuf[numlen]));
1670 2 : break;
1671 : }
1672 0 : default:
1673 : {
1674 0 : vlen[i] = 0;
1675 0 : break;
1676 : }
1677 : }
1678 : }
1679 27 : overflow:
1680 27 : if(space)
1681 : {
1682 12 : len += std::max(prefix ? i : i-1, 0);
1683 : }
1684 27 : char *buf = newstring(len + numlen);
1685 27 : int offset = 0,
1686 27 : numoffset = 0;
1687 27 : if(prefix)
1688 : {
1689 4 : std::memcpy(buf, prefix, prefixlen);
1690 4 : offset += prefixlen;
1691 4 : if(space && i)
1692 : {
1693 2 : buf[offset++] = ' ';
1694 : }
1695 : }
1696 61 : for(int j = 0; j < i; ++j)
1697 : {
1698 57 : if(v[j].type == Value_Integer || v[j].type == Value_Float)
1699 : {
1700 10 : std::memcpy(&buf[offset], &numbuf[numoffset], vlen[j]);
1701 10 : numoffset += vlen[j];
1702 : }
1703 47 : else if(vlen[j])
1704 : {
1705 47 : std::memcpy(&buf[offset], v[j].s, vlen[j]);
1706 : }
1707 57 : offset += vlen[j];
1708 57 : if(j==i-1)
1709 : {
1710 23 : break;
1711 : }
1712 34 : if(space)
1713 : {
1714 16 : buf[offset++] = ' ';
1715 : }
1716 : }
1717 27 : buf[offset] = '\0';
1718 27 : if(i < n)
1719 : {
1720 0 : char *morebuf = conc(&v[i], n-i, space, buf, offset);
1721 0 : delete[] buf;
1722 0 : return morebuf;
1723 : }
1724 27 : return buf;
1725 : }
1726 :
1727 23 : char *conc(const tagval *v, int n, bool space)
1728 : {
1729 23 : return conc(v, n, space, nullptr, 0);
1730 : }
1731 :
1732 4 : char *conc(const tagval *v, int n, bool space, const char *prefix)
1733 : {
1734 4 : return conc(v, n, space, prefix, std::strlen(prefix));
1735 : }
1736 :
1737 : //ignore double slashes in cubescript lines
1738 9219 : static void skipcomments(const char *&p)
1739 : {
1740 : for(;;)
1741 : {
1742 9219 : p += std::strspn(p, " \t\r");
1743 9219 : if(p[0]!='/' || p[1]!='/')
1744 : {
1745 : break;
1746 : }
1747 1 : p += std::strcspn(p, "\n\0");
1748 : }
1749 9218 : }
1750 :
1751 0 : static void cutstring(const char *&p, stringslice &s)
1752 : {
1753 0 : p++;
1754 0 : const char *end = parsestring(p);
1755 0 : uint maxlen = (end-p) + 1;
1756 :
1757 0 : stridx = (stridx + 1)%4;
1758 0 : std::vector<char> &buf = strbuf[stridx];
1759 0 : if(buf.capacity() < maxlen)
1760 : {
1761 0 : buf.reserve(maxlen);
1762 : }
1763 0 : s.str = buf.data();
1764 0 : s.len = unescapestring(buf.data(), p, end);
1765 0 : p = end;
1766 0 : if(*p=='\"')
1767 : {
1768 0 : p++;
1769 : }
1770 0 : }
1771 :
1772 0 : static char *cutstring(const char *&p)
1773 : {
1774 0 : p++;
1775 0 : const char *end = parsestring(p);
1776 0 : char *buf = newstring(end-p);
1777 0 : unescapestring(buf, p, end);
1778 0 : p = end;
1779 0 : if(*p=='\"')
1780 : {
1781 0 : p++;
1782 : }
1783 0 : return buf;
1784 : }
1785 :
1786 5445 : const char *parseword(const char *p)
1787 : {
1788 5445 : constexpr int maxbrak = 100;
1789 : static char brakstack[maxbrak];
1790 5445 : int brakdepth = 0;
1791 100 : for(;; p++)
1792 : {
1793 5545 : p += std::strcspn(p, "\"/;()[] \t\r\n\0");
1794 5545 : switch(p[0])
1795 : {
1796 5277 : case '"':
1797 : case ';':
1798 : case ' ':
1799 : case '\t':
1800 : case '\r':
1801 : case '\n':
1802 : case '\0':
1803 : {
1804 5277 : return p;
1805 : }
1806 0 : case '/':
1807 : {
1808 0 : if(p[1] == '/')
1809 : {
1810 0 : return p;
1811 : }
1812 0 : break;
1813 : }
1814 : //change depth of bracket stack upon seeing a (, [ char
1815 101 : case '[':
1816 : case '(':
1817 : {
1818 101 : if(brakdepth >= maxbrak)
1819 : {
1820 1 : return p;
1821 : }
1822 100 : brakstack[brakdepth++] = p[0];
1823 100 : break;
1824 : }
1825 127 : case ']':
1826 : {
1827 127 : if(brakdepth <= 0 || brakstack[--brakdepth] != '[')
1828 : {
1829 127 : return p;
1830 : }
1831 0 : break;
1832 : }
1833 : //parens get treated seperately
1834 40 : case ')':
1835 : {
1836 40 : if(brakdepth <= 0 || brakstack[--brakdepth] != '(')
1837 : {
1838 40 : return p;
1839 : }
1840 0 : break;
1841 : }
1842 : }
1843 : }
1844 : return p;
1845 : }
1846 :
1847 4559 : static void cutword(const char *&p, stringslice &s)
1848 : {
1849 4559 : s.str = p;
1850 4559 : p = parseword(p);
1851 4559 : s.len = static_cast<int>(p-s.str);
1852 4559 : }
1853 :
1854 120 : static char *cutword(const char *&p)
1855 : {
1856 120 : const char *word = p;
1857 120 : p = parseword(p);
1858 120 : return p!=word ? newstring(word, p-word) : nullptr;
1859 : }
1860 :
1861 : /**
1862 : * @brief The functions compares the `type` with other types from an enum. Types
1863 : * lower than Value_Any are primitives, so we are not interested in those.
1864 : * @param type the kind of token, such as code, macro, indent, csrting, etc.
1865 : * @param defaultret the default return type flag to return (either null, integer, or float).
1866 : * @return int the type flag
1867 : */
1868 1304 : int ret_code(int type, int defaultret)
1869 : {
1870 : // If value is bigger than a primitive:
1871 1304 : if(type >= Value_Any)
1872 : {
1873 : // If the value type is CString:
1874 1304 : if(type == Value_CString)
1875 : {
1876 : // Return string type flag.
1877 2 : return Ret_String;
1878 : }
1879 :
1880 : // Return the default type flag.
1881 1302 : return defaultret;
1882 : }
1883 : // Return type * 2 ^ Core_Ret.
1884 0 : return type << Code_Ret;
1885 : }
1886 :
1887 0 : int ret_code_int(int type)
1888 : {
1889 0 : return ret_code(type, Ret_Integer);
1890 : }
1891 :
1892 0 : int ret_code_float(int type)
1893 : {
1894 0 : return ret_code(type, Ret_Float);
1895 : }
1896 :
1897 1304 : int ret_code_any(int type)
1898 : {
1899 1304 : return ret_code(type, 0);
1900 : }
1901 :
1902 : /**
1903 : * @brief Return a string type flag for any type that is not a primitive.
1904 : * @param type the kind of token, such as code, macro, indent, csrting, etc.
1905 : * @return int the type flag
1906 : */
1907 81 : int ret_code_string(int type)
1908 : {
1909 : // If value is bigger than a primitive:
1910 81 : if(type >= Value_Any)
1911 : {
1912 : // Return string type flag.
1913 37 : return Ret_String;
1914 : }
1915 :
1916 : // Return type * 2 ^ Core_Ret.
1917 44 : return type << Code_Ret;
1918 : }
1919 :
1920 555 : static void compilestr(std::vector<uint> &code, const char *word, int len, bool macro = false)
1921 : {
1922 555 : if(len <= 3 && !macro)
1923 : {
1924 45 : uint op = Code_ValI|Ret_String;
1925 94 : for(int i = 0; i < len; ++i)
1926 : {
1927 49 : op |= static_cast<uint>(static_cast<uchar>(word[i]))<<((i+1)*8);
1928 : }
1929 45 : code.push_back(op);
1930 45 : return;
1931 : }
1932 510 : code.push_back((macro ? Code_Macro : Code_Val|Ret_String));
1933 510 : code.back() |= len << 8;
1934 686 : for(uint i = 0; i < len/sizeof(uint); ++i)
1935 : {
1936 176 : code.push_back((reinterpret_cast<const uint *>(word))[i]);
1937 : }
1938 510 : size_t endlen = len%sizeof(uint);
1939 : union
1940 : {
1941 : char c[sizeof(uint)];
1942 : uint u;
1943 : } end;
1944 510 : end.u = 0;
1945 510 : std::memcpy(end.c, word + len - endlen, endlen);
1946 510 : code.push_back(end.u);
1947 : }
1948 :
1949 0 : static void compilestr(std::vector<uint> &code)
1950 : {
1951 0 : code.push_back(Code_ValI|Ret_String);
1952 0 : }
1953 :
1954 245 : static void compilestr(std::vector<uint> &code, const stringslice &word, bool macro = false)
1955 : {
1956 245 : compilestr(code, word.str, word.len, macro);
1957 245 : }
1958 :
1959 : //compile un-escape string
1960 : // removes the escape characters from a c string reference `p` (such as "\")
1961 : // and appends it to the execution string referenced to by code. The len/(val/ret/macro) field
1962 : // of the code vector is created depending on the state of macro and the length of the string
1963 : // passed.
1964 113 : static void compileunescapestring(std::vector<uint> &code, const char *&p, bool macro = false)
1965 : {
1966 113 : p++;
1967 113 : const char *end = parsestring(p);
1968 113 : code.emplace_back(macro ? Code_Macro : Code_Val|Ret_String);
1969 113 : int size = static_cast<int>(end-p)/sizeof(uint) + 1;
1970 113 : int oldvecsize = code.size();
1971 501 : for(int i = 0; i < size; ++i)
1972 : {
1973 388 : code.emplace_back();
1974 : }
1975 113 : char *buf = reinterpret_cast<char *>(&(code[oldvecsize]));
1976 113 : int len = unescapestring(buf, p, end);
1977 113 : std::memset(&buf[len], 0, sizeof(uint) - len%sizeof(uint));
1978 113 : code.at(oldvecsize-1) |= len<<8;
1979 113 : p = end;
1980 113 : if(*p == '\"')
1981 : {
1982 111 : p++;
1983 : }
1984 113 : }
1985 837 : static void compileint(std::vector<uint> &code, int i = 0)
1986 : {
1987 837 : if(i >= -0x800000 && i <= 0x7FFFFF)
1988 : {
1989 820 : code.push_back(Code_ValI|Ret_Integer|(i<<8));
1990 : }
1991 : else
1992 : {
1993 17 : code.push_back(Code_Val|Ret_Integer);
1994 17 : code.push_back(i);
1995 : }
1996 837 : }
1997 :
1998 83 : static void compilenull(std::vector<uint> &code)
1999 : {
2000 83 : code.push_back(Code_ValI|Ret_Null);
2001 83 : }
2002 :
2003 : static uint emptyblock[Value_Any][2] =
2004 : {
2005 : { Code_Start + 0x100, Code_Exit|Ret_Null },
2006 : { Code_Start + 0x100, Code_Exit|Ret_Integer },
2007 : { Code_Start + 0x100, Code_Exit|Ret_Float },
2008 : { Code_Start + 0x100, Code_Exit|Ret_String }
2009 : };
2010 :
2011 173 : static void compileblock(std::vector<uint> &code)
2012 : {
2013 173 : code.push_back(Code_Empty);
2014 173 : }
2015 :
2016 : static void compilestatements(std::vector<uint> &code, const char *&p, int rettype, int brak = '\0', int prevargs = 0);
2017 :
2018 124 : static const char *compileblock(std::vector<uint> &code, const char *p, int rettype = Ret_Null, int brak = '\0')
2019 : {
2020 124 : uint start = code.size();
2021 124 : code.push_back(Code_Block);
2022 124 : code.push_back(Code_Offset|((start+2)<<8));
2023 124 : if(p)
2024 : {
2025 124 : compilestatements(code, p, Value_Any, brak);
2026 : }
2027 124 : if(code.size() > start + 2)
2028 : {
2029 124 : code.push_back(Code_Exit|rettype);
2030 124 : code[start] |= static_cast<uint>(code.size() - (start + 1))<<8;
2031 : }
2032 : else
2033 : {
2034 0 : code.resize(start);
2035 0 : code.push_back(Code_Empty|rettype);
2036 : }
2037 124 : return p;
2038 : }
2039 :
2040 130 : static void compileident(std::vector<uint> &code, ident *id = dummyident)
2041 : {
2042 130 : code.push_back((id->index < Max_Args ? Code_IdentArg : Code_Ident)|(id->index<<8));
2043 130 : }
2044 :
2045 92 : static void compileident(std::vector<uint> &code, const stringslice &word)
2046 : {
2047 184 : std::string lookupsubstr = std::string(word.str).substr(0, word.len);
2048 92 : compileident(code, newident(lookupsubstr.c_str(), Idf_Unknown));
2049 92 : }
2050 :
2051 484 : static void compileint(std::vector<uint> &code, const stringslice &word)
2052 : {
2053 968 : std::string lookupsubstr = std::string(word.str).substr(0, word.len);
2054 484 : compileint(code, word.len ? parseint(lookupsubstr.c_str()) : 0);
2055 484 : }
2056 :
2057 620 : static void compilefloat(std::vector<uint> &code, float f = 0.0f)
2058 : {
2059 620 : if(static_cast<int>(f) == f && f >= -0x800000 && f <= 0x7FFFFF)
2060 : {
2061 588 : code.push_back(Code_ValI|Ret_Float|(static_cast<int>(f)<<8));
2062 : }
2063 : else
2064 : {
2065 : union
2066 : {
2067 : float f;
2068 : uint u;
2069 : } conv;
2070 32 : conv.f = f;
2071 32 : code.push_back(Code_Val|Ret_Float);
2072 32 : code.push_back(conv.u);
2073 : }
2074 620 : }
2075 :
2076 247 : static void compilefloat(std::vector<uint> &code, const stringslice &word)
2077 : {
2078 247 : compilefloat(code, word.len ? parsefloat(word.str) : 0.0f);
2079 247 : }
2080 :
2081 122 : bool getbool(const tagval &v)
2082 : {
2083 17 : auto getbool = [] (const char *s)
2084 : {
2085 17 : switch(s[0])
2086 : {
2087 1 : case '+':
2088 : case '-':
2089 1 : switch(s[1])
2090 : {
2091 0 : case '0':
2092 : {
2093 0 : break;
2094 : }
2095 0 : case '.':
2096 : {
2097 0 : return !isdigit(s[2]) || parsefloat(s) != 0;
2098 : }
2099 1 : default:
2100 : {
2101 1 : return true;
2102 : }
2103 : }
2104 : [[fallthrough]];
2105 : case '0':
2106 : {
2107 : char *end;
2108 7 : int val = static_cast<int>(std::strtoul(const_cast<char *>(s), &end, 0));
2109 7 : if(val)
2110 : {
2111 0 : return true;
2112 : }
2113 7 : switch(*end)
2114 : {
2115 2 : case 'e':
2116 : case '.':
2117 : {
2118 2 : return parsefloat(s) != 0;
2119 : }
2120 5 : default:
2121 : {
2122 5 : return false;
2123 : }
2124 : }
2125 : }
2126 0 : case '.':
2127 : {
2128 0 : return !isdigit(s[1]) || parsefloat(s) != 0;
2129 : }
2130 0 : case '\0':
2131 : {
2132 0 : return false;
2133 : }
2134 9 : default:
2135 : {
2136 9 : return true;
2137 : }
2138 : }
2139 : };
2140 :
2141 122 : switch(v.type)
2142 : {
2143 0 : case Value_Float:
2144 : {
2145 0 : return v.f!=0;
2146 : }
2147 94 : case Value_Integer:
2148 : {
2149 94 : return v.i!=0;
2150 : }
2151 17 : case Value_String:
2152 : case Value_Macro:
2153 : case Value_CString:
2154 : {
2155 17 : return getbool(v.s);
2156 : }
2157 11 : default:
2158 : {
2159 11 : return false;
2160 : }
2161 : }
2162 : }
2163 :
2164 1068 : static void compileval(std::vector<uint> &code, int wordtype, const stringslice &word = stringslice(nullptr, 0))
2165 : {
2166 1068 : switch(wordtype)
2167 : {
2168 49 : case Value_CAny:
2169 : {
2170 49 : if(word.len)
2171 : {
2172 49 : compilestr(code, word, true);
2173 : }
2174 : else
2175 : {
2176 0 : compilenull(code);
2177 : }
2178 49 : break;
2179 : }
2180 147 : case Value_CString:
2181 : {
2182 147 : compilestr(code, word, true);
2183 147 : break;
2184 : }
2185 49 : case Value_Any:
2186 : {
2187 49 : if(word.len)
2188 : {
2189 49 : compilestr(code, word);
2190 : }
2191 : else
2192 : {
2193 0 : compilenull(code);
2194 : }
2195 49 : break;
2196 : }
2197 0 : case Value_String:
2198 : {
2199 0 : compilestr(code, word);
2200 0 : break;
2201 : }
2202 247 : case Value_Float:
2203 : {
2204 247 : compilefloat(code, word);
2205 247 : break;
2206 : }
2207 484 : case Value_Integer:
2208 : {
2209 484 : compileint(code, word);
2210 484 : break;
2211 : }
2212 0 : case Value_Cond:
2213 : {
2214 0 : if(word.len)
2215 : {
2216 0 : compileblock(code, word.str);
2217 : }
2218 : else
2219 : {
2220 0 : compilenull(code);
2221 : }
2222 0 : break;
2223 : }
2224 0 : case Value_Code:
2225 : {
2226 0 : compileblock(code, word.str);
2227 0 : break;
2228 : }
2229 92 : case Value_Ident:
2230 : {
2231 92 : compileident(code, word);
2232 92 : break;
2233 : }
2234 0 : default:
2235 : {
2236 0 : break;
2237 : }
2238 : }
2239 1068 : }
2240 :
2241 : static stringslice unusedword(nullptr, 0);
2242 : static bool compilearg(std::vector<uint> &code, const char *&p, int wordtype, int prevargs = Max_Results, stringslice &word = unusedword);
2243 :
2244 127 : static void compilelookup(std::vector<uint> &code, const char *&p, int ltype, int prevargs = Max_Results)
2245 : {
2246 127 : stringslice lookup;
2247 127 : switch(*++p)
2248 : {
2249 0 : case '(':
2250 : case '[':
2251 : {
2252 0 : if(!compilearg(code, p, Value_CString, prevargs))
2253 : {
2254 0 : goto invalid;
2255 : }
2256 0 : break;
2257 : }
2258 0 : case '$':
2259 : {
2260 0 : compilelookup(code, p, Value_CString, prevargs);
2261 0 : break;
2262 : }
2263 0 : case '\"':
2264 : {
2265 0 : cutstring(p, lookup);
2266 0 : goto lookupid; //immediately below, part of default case
2267 : }
2268 127 : default:
2269 : {
2270 127 : cutword(p, lookup);
2271 127 : if(!lookup.len)
2272 : {
2273 0 : goto invalid; //invalid is near bottom of fxn
2274 : }
2275 127 : lookupid:
2276 254 : std::string lookupsubstr = std::string(lookup.str).substr(0, lookup.len);
2277 127 : ident *id = newident(lookupsubstr.c_str(), Idf_Unknown);
2278 127 : if(id)
2279 : {
2280 127 : switch(id->type)
2281 : {
2282 0 : case Id_Var:
2283 : {
2284 0 : code.push_back(Code_IntVar|ret_code_int(ltype)|(id->index<<8));
2285 : switch(ltype)
2286 : {
2287 0 : case Value_Pop:
2288 : {
2289 0 : code.pop_back();
2290 0 : break;
2291 : }
2292 0 : case Value_Code:
2293 : {
2294 0 : code.push_back(Code_Compile);
2295 0 : break;
2296 : }
2297 0 : case Value_Ident:
2298 : {
2299 0 : code.push_back(Code_IdentU);
2300 0 : break;
2301 : }
2302 : }
2303 0 : return;
2304 : }
2305 0 : case Id_FloatVar:
2306 : {
2307 0 : code.push_back(Code_FloatVar|ret_code_float(ltype)|(id->index<<8));
2308 : switch(ltype)
2309 : {
2310 0 : case Value_Pop:
2311 : {
2312 0 : code.pop_back();
2313 0 : break;
2314 : }
2315 0 : case Value_Code:
2316 : {
2317 0 : code.push_back(Code_Compile);
2318 0 : break;
2319 : }
2320 0 : case Value_Ident:
2321 : {
2322 0 : code.push_back(Code_IdentU);
2323 0 : break;
2324 : }
2325 : }
2326 0 : return;
2327 : }
2328 0 : case Id_StringVar:
2329 : {
2330 : switch(ltype)
2331 : {
2332 0 : case Value_Pop:
2333 : {
2334 0 : return;
2335 : }
2336 0 : case Value_CAny:
2337 : case Value_CString:
2338 : case Value_Code:
2339 : case Value_Ident:
2340 : case Value_Cond:
2341 : {
2342 0 : code.push_back(Code_StrVarM|(id->index<<8));
2343 0 : break;
2344 : }
2345 0 : default:
2346 : {
2347 0 : code.push_back(Code_StrVar|ret_code_string(ltype)|(id->index<<8));
2348 0 : break;
2349 : }
2350 : }
2351 0 : goto done;
2352 : }
2353 126 : case Id_Alias:
2354 : {
2355 : switch(ltype)
2356 : {
2357 0 : case Value_Pop:
2358 : {
2359 0 : return;
2360 : }
2361 0 : case Value_CAny:
2362 : case Value_Cond:
2363 : {
2364 0 : code.push_back((id->index < Max_Args ? Code_LookupMArg : Code_LookupM)|(id->index<<8));
2365 0 : break;
2366 : }
2367 45 : case Value_CString:
2368 : case Value_Code:
2369 : case Value_Ident:
2370 : {
2371 45 : code.push_back((id->index < Max_Args ? Code_LookupMArg : Code_LookupM)|Ret_String|(id->index<<8));
2372 45 : break;
2373 : }
2374 81 : default:
2375 : {
2376 81 : code.push_back((id->index < Max_Args ? Code_LookupArg : Code_Lookup)|ret_code_string(ltype)|(id->index<<8));
2377 81 : break;
2378 : }
2379 : }
2380 126 : goto done;
2381 : }
2382 1 : case Id_Command:
2383 : {
2384 1 : int comtype = Code_Com,
2385 1 : numargs = 0;
2386 1 : if(prevargs >= Max_Results)
2387 : {
2388 0 : code.push_back(Code_Enter);
2389 : }
2390 4 : for(const char *fmt = id->args; *fmt; fmt++)
2391 : {
2392 3 : switch(*fmt)
2393 : {
2394 0 : case 'S':
2395 : {
2396 0 : compilestr(code);
2397 0 : numargs++;
2398 0 : break;
2399 : }
2400 1 : case 's':
2401 : {
2402 1 : compilestr(code, nullptr, 0, true);
2403 1 : numargs++;
2404 1 : break;
2405 : }
2406 0 : case 'i':
2407 : {
2408 0 : compileint(code);
2409 0 : numargs++;
2410 0 : break;
2411 : }
2412 0 : case 'b':
2413 : {
2414 0 : compileint(code, INT_MIN);
2415 0 : numargs++;
2416 0 : break;
2417 : }
2418 0 : case 'f':
2419 : {
2420 0 : compilefloat(code);
2421 0 : numargs++;
2422 0 : break;
2423 : }
2424 0 : case 'F':
2425 : {
2426 0 : code.push_back(Code_Dup|Ret_Float);
2427 0 : numargs++;
2428 0 : break;
2429 : }
2430 0 : case 'E':
2431 : case 'T':
2432 : case 't':
2433 : {
2434 0 : compilenull(code);
2435 0 : numargs++;
2436 0 : break;
2437 : }
2438 1 : case 'e':
2439 : {
2440 1 : compileblock(code);
2441 1 : numargs++;
2442 1 : break;
2443 : }
2444 1 : case 'r':
2445 : {
2446 1 : compileident(code);
2447 1 : numargs++;
2448 1 : break;
2449 : }
2450 0 : case '$':
2451 : {
2452 0 : compileident(code, id);
2453 0 : numargs++;
2454 0 : break;
2455 : }
2456 0 : case 'N':
2457 : {
2458 0 : compileint(code, -1);
2459 0 : numargs++;
2460 0 : break;
2461 : }
2462 0 : case 'D':
2463 : {
2464 0 : comtype = Code_ComD;
2465 0 : numargs++;
2466 0 : break;
2467 : }
2468 0 : case 'C':
2469 : {
2470 0 : comtype = Code_ComC;
2471 0 : goto compilecomv; //compilecomv beneath this switch statement
2472 : }
2473 0 : case 'V':
2474 : {
2475 0 : comtype = Code_ComV;
2476 0 : goto compilecomv;
2477 : }
2478 0 : case '1':
2479 : case '2':
2480 : case '3':
2481 : case '4':
2482 : {
2483 0 : break;
2484 : }
2485 : }
2486 : }
2487 1 : code.push_back(comtype|ret_code_any(ltype)|(id->index<<8));
2488 1 : code.push_back((prevargs >= Max_Results ? Code_Exit : Code_ResultArg) | ret_code_any(ltype));
2489 1 : goto done;
2490 0 : compilecomv:
2491 0 : code.push_back(comtype|ret_code_any(ltype)|(numargs<<8)|(id->index<<13));
2492 0 : code.push_back((prevargs >= Max_Results ? Code_Exit : Code_ResultArg) | ret_code_any(ltype));
2493 0 : goto done;
2494 : }
2495 0 : default:
2496 : {
2497 0 : goto invalid;
2498 : }
2499 : }
2500 : compilestr(code, lookup, true);
2501 : break;
2502 : }
2503 : }
2504 : }
2505 0 : switch(ltype)
2506 : {
2507 0 : case Value_CAny:
2508 : case Value_Cond:
2509 : {
2510 0 : code.push_back(Code_LookupMU);
2511 0 : break;
2512 : }
2513 0 : case Value_CString:
2514 : case Value_Code:
2515 : case Value_Ident:
2516 : {
2517 0 : code.push_back(Code_LookupMU|Ret_String);
2518 0 : break;
2519 : }
2520 0 : default:
2521 : {
2522 0 : code.push_back(Code_LookupU|ret_code_any(ltype));
2523 0 : break;
2524 : }
2525 : }
2526 127 : done:
2527 127 : switch(ltype)
2528 : {
2529 0 : case Value_Pop:
2530 : {
2531 0 : code.push_back(Code_Pop);
2532 0 : break;
2533 : }
2534 0 : case Value_Code:
2535 : {
2536 0 : code.push_back(Code_Compile);
2537 0 : break;
2538 : }
2539 0 : case Value_Cond:
2540 : {
2541 0 : code.push_back(Code_Cond);
2542 0 : break;
2543 : }
2544 0 : case Value_Ident:
2545 : {
2546 0 : code.push_back(Code_IdentU);
2547 0 : break;
2548 : }
2549 : }
2550 127 : return;
2551 0 : invalid:
2552 0 : switch(ltype)
2553 : {
2554 0 : case Value_Pop:
2555 : {
2556 0 : break;
2557 : }
2558 0 : case Value_Null:
2559 : case Value_Any:
2560 : case Value_CAny:
2561 : case Value_Word:
2562 : case Value_Cond:
2563 : {
2564 0 : compilenull(code);
2565 0 : break;
2566 : }
2567 0 : default:
2568 : {
2569 0 : compileval(code, ltype);
2570 0 : break;
2571 : }
2572 : }
2573 : }
2574 :
2575 7 : static bool compileblockstr(std::vector<uint> &code, const char *str, const char *end, bool macro)
2576 : {
2577 7 : int start = code.size();
2578 7 : code.push_back(macro ? Code_Macro : Code_Val|Ret_String);
2579 7 : int size = (end-str)/sizeof(uint)+1;
2580 7 : int oldvecsize = code.size();
2581 41 : for(int i = 0; i < size; ++i)
2582 : {
2583 34 : code.emplace_back();
2584 : }
2585 7 : char *buf = reinterpret_cast<char *>(&(code[oldvecsize]));
2586 7 : int len = 0;
2587 8 : while(str < end)
2588 : {
2589 8 : int n = std::strcspn(str, "\r/\"@]\0");
2590 8 : std::memcpy(&buf[len], str, n);
2591 8 : len += n;
2592 8 : str += n;
2593 8 : switch(*str)
2594 : {
2595 0 : case '\r':
2596 : {
2597 0 : str++;
2598 0 : break;
2599 : }
2600 0 : case '\"':
2601 : {
2602 0 : const char *start = str;
2603 0 : str = parsestring(str+1);
2604 0 : if(*str=='\"')
2605 : {
2606 0 : str++;
2607 : }
2608 0 : std::memcpy(&buf[len], start, str-start);
2609 0 : len += str-start;
2610 0 : break;
2611 : }
2612 0 : case '/':
2613 0 : if(str[1] == '/')
2614 : {
2615 0 : size_t comment = std::strcspn(str, "\n\0");
2616 0 : if (iscubepunct(str[2]))
2617 : {
2618 0 : std::memcpy(&buf[len], str, comment);
2619 0 : len += comment;
2620 : }
2621 0 : str += comment;
2622 : }
2623 : else
2624 : {
2625 0 : buf[len++] = *str++;
2626 : }
2627 0 : break;
2628 8 : case '@':
2629 : case ']':
2630 8 : if(str < end)
2631 : {
2632 1 : buf[len++] = *str++;
2633 1 : break;
2634 : }
2635 : case '\0':
2636 : {
2637 7 : goto done;
2638 : }
2639 : }
2640 : }
2641 0 : done:
2642 7 : std::memset(&buf[len], '\0', sizeof(uint)-len%sizeof(uint));
2643 7 : code[start] |= len<<8;
2644 7 : return true;
2645 : }
2646 :
2647 0 : static bool compileblocksub(std::vector<uint> &code, const char *&p, int prevargs)
2648 : {
2649 0 : stringslice lookup;
2650 0 : switch(*p)
2651 : {
2652 0 : case '(':
2653 : {
2654 0 : if(!compilearg(code, p, Value_CAny, prevargs))
2655 : {
2656 0 : return false;
2657 : }
2658 0 : break;
2659 : }
2660 0 : case '[':
2661 : {
2662 0 : if(!compilearg(code, p, Value_CString, prevargs))
2663 : {
2664 0 : return false;
2665 : }
2666 0 : code.push_back(Code_LookupMU);
2667 0 : break;
2668 : }
2669 0 : case '\"':
2670 : {
2671 0 : cutstring(p, lookup);
2672 0 : goto lookupid;
2673 : }
2674 0 : default:
2675 : {
2676 :
2677 0 : lookup.str = p;
2678 0 : while(iscubealnum(*p) || *p=='_')
2679 : {
2680 0 : p++;
2681 : }
2682 0 : lookup.len = static_cast<int>(p-lookup.str);
2683 0 : if(!lookup.len)
2684 : {
2685 0 : return false;
2686 : }
2687 0 : lookupid:
2688 0 : std::string lookupsubstr = std::string(lookup.str).substr(0, lookup.len);
2689 0 : ident *id = newident(lookupsubstr.c_str(), Idf_Unknown);
2690 0 : if(id)
2691 : {
2692 0 : switch(id->type)
2693 : {
2694 0 : case Id_Var:
2695 : {
2696 0 : code.push_back(Code_IntVar|(id->index<<8));
2697 0 : goto done;
2698 : }
2699 0 : case Id_FloatVar:
2700 : {
2701 0 : code.push_back(Code_FloatVar|(id->index<<8));
2702 0 : goto done;
2703 : }
2704 0 : case Id_StringVar:
2705 : {
2706 0 : code.push_back(Code_StrVarM|(id->index<<8));
2707 0 : goto done;
2708 : }
2709 0 : case Id_Alias:
2710 : {
2711 0 : code.push_back((id->index < Max_Args ? Code_LookupMArg : Code_LookupM)|(id->index<<8));
2712 0 : goto done;
2713 : }
2714 : }
2715 : }
2716 0 : compilestr(code, lookup, true);
2717 0 : code.push_back(Code_LookupMU);
2718 0 : done:
2719 0 : break;
2720 : }
2721 : }
2722 0 : return true;
2723 : }
2724 :
2725 82 : static void compileblockmain(std::vector<uint> &code, const char *&p, int wordtype, int prevargs)
2726 : {
2727 82 : const char *line = p,
2728 82 : *start = p;
2729 82 : int concs = 0;
2730 170 : for(int brak = 1; brak;)
2731 : {
2732 88 : p += std::strcspn(p, "@\"/[]\0");
2733 88 : int c = *p++;
2734 88 : switch(c)
2735 : {
2736 0 : case '\0':
2737 : {
2738 0 : debugcodeline(line, "missing \"]\"");
2739 0 : p--;
2740 0 : goto done;
2741 : }
2742 0 : case '\"':
2743 : {
2744 0 : p = parsestring(p);
2745 0 : if(*p=='\"')
2746 : {
2747 0 : p++;
2748 : }
2749 0 : break;
2750 : }
2751 0 : case '/':
2752 0 : if(*p=='/')
2753 : {
2754 0 : p += std::strcspn(p, "\n\0");
2755 : }
2756 0 : break;
2757 3 : case '[':
2758 : {
2759 3 : brak++;
2760 3 : break;
2761 : }
2762 85 : case ']':
2763 : {
2764 85 : brak--;
2765 85 : break;
2766 : }
2767 0 : case '@':
2768 : {
2769 0 : const char *esc = p;
2770 0 : while(*p == '@')
2771 : {
2772 0 : p++;
2773 : }
2774 0 : int level = p - (esc - 1);
2775 0 : if(brak > level)
2776 : {
2777 0 : continue;
2778 : }
2779 0 : else if(brak < level)
2780 : {
2781 0 : debugcodeline(line, "too many @s");
2782 : }
2783 0 : if(!concs && prevargs >= Max_Results)
2784 : {
2785 0 : code.push_back(Code_Enter);
2786 : }
2787 0 : if(concs + 2 > Max_Args)
2788 : {
2789 0 : code.push_back(Code_ConCW|Ret_String|(concs<<8));
2790 0 : concs = 1;
2791 : }
2792 0 : if(compileblockstr(code, start, esc-1, true))
2793 : {
2794 0 : concs++;
2795 : }
2796 0 : if(compileblocksub(code, p, prevargs + concs))
2797 : {
2798 0 : concs++;
2799 : }
2800 0 : if(concs)
2801 : {
2802 0 : start = p;
2803 : }
2804 0 : else if(prevargs >= Max_Results)
2805 : {
2806 0 : code.pop_back();
2807 : }
2808 0 : break;
2809 : }
2810 : }
2811 : }
2812 82 : done:
2813 82 : if(p-1 > start)
2814 : {
2815 81 : if(!concs)
2816 : {
2817 81 : switch(wordtype)
2818 : {
2819 0 : case Value_Pop:
2820 : {
2821 0 : return;
2822 : }
2823 74 : case Value_Code:
2824 : case Value_Cond:
2825 : {
2826 74 : p = compileblock(code, start, Ret_Null, ']');
2827 74 : return;
2828 : }
2829 0 : case Value_Ident:
2830 : {
2831 0 : compileident(code, stringslice(start, p-1));
2832 0 : return;
2833 : }
2834 : }
2835 : }
2836 7 : switch(wordtype)
2837 : {
2838 7 : case Value_CString:
2839 : case Value_Code:
2840 : case Value_Ident:
2841 : case Value_CAny:
2842 : case Value_Cond:
2843 : {
2844 7 : compileblockstr(code, start, p-1, true);
2845 7 : break;
2846 : }
2847 0 : default:
2848 : {
2849 0 : compileblockstr(code, start, p-1, concs > 0);
2850 0 : break;
2851 : }
2852 : }
2853 7 : if(concs > 1)
2854 : {
2855 0 : concs++;
2856 : }
2857 : }
2858 8 : if(concs)
2859 : {
2860 0 : if(prevargs >= Max_Results)
2861 : {
2862 0 : code.push_back(Code_ConCM|ret_code_any(wordtype)|(concs<<8));
2863 0 : code.push_back(Code_Exit|ret_code_any(wordtype));
2864 : }
2865 : else
2866 : {
2867 0 : code.push_back(Code_ConCW|ret_code_any(wordtype)|(concs<<8));
2868 : }
2869 : }
2870 8 : switch(wordtype)
2871 : {
2872 0 : case Value_Pop:
2873 : {
2874 0 : if(concs || p-1 > start)
2875 : {
2876 0 : code.push_back(Code_Pop);
2877 : }
2878 0 : break;
2879 : }
2880 0 : case Value_Cond:
2881 : {
2882 0 : if(!concs && p-1 <= start)
2883 : {
2884 0 : compilenull(code);
2885 : }
2886 : else
2887 : {
2888 0 : code.push_back(Code_Cond);
2889 : }
2890 0 : break;
2891 : }
2892 1 : case Value_Code:
2893 : {
2894 1 : if(!concs && p-1 <= start)
2895 : {
2896 1 : compileblock(code);
2897 : }
2898 : else
2899 : {
2900 0 : code.push_back(Code_Compile);
2901 : }
2902 1 : break;
2903 : }
2904 0 : case Value_Ident:
2905 : {
2906 0 : if(!concs && p-1 <= start)
2907 : {
2908 0 : compileident(code);
2909 : }
2910 : else
2911 : {
2912 0 : code.push_back(Code_IdentU);
2913 : }
2914 0 : break;
2915 : }
2916 7 : case Value_CString:
2917 : case Value_CAny:
2918 : {
2919 7 : if(!concs && p-1 <= start)
2920 : {
2921 0 : compilestr(code, nullptr, 0, true);
2922 : }
2923 7 : break;
2924 : }
2925 0 : case Value_String:
2926 : case Value_Null:
2927 : case Value_Any:
2928 : case Value_Word:
2929 : {
2930 0 : if(!concs && p-1 <= start)
2931 : {
2932 0 : compilestr(code);
2933 : }
2934 0 : break;
2935 : }
2936 0 : default:
2937 : {
2938 0 : if(!concs)
2939 : {
2940 0 : if(p-1 <= start)
2941 : {
2942 0 : compileval(code, wordtype);
2943 : }
2944 : else
2945 : {
2946 0 : code.push_back(Code_Force|(wordtype<<Code_Ret));
2947 : }
2948 : }
2949 0 : break;
2950 : }
2951 : }
2952 : }
2953 :
2954 5339 : static bool compilearg(std::vector<uint> &code, const char *&p, int wordtype, int prevargs, stringslice &word)
2955 : {
2956 5339 : skipcomments(p);
2957 5339 : switch(*p)
2958 : {
2959 : //cases for special chars: \[]()$
2960 113 : case '\"':
2961 : {
2962 : switch(wordtype)
2963 : {
2964 0 : case Value_Pop:
2965 : {
2966 0 : p = parsestring(p+1);
2967 0 : if(*p == '\"')
2968 : {
2969 0 : p++;
2970 : }
2971 0 : break;
2972 : }
2973 0 : case Value_Cond:
2974 : {
2975 0 : char *s = cutstring(p);
2976 0 : if(s[0])
2977 : {
2978 0 : compileblock(code, s);
2979 : }
2980 : else
2981 : {
2982 0 : compilenull(code);
2983 : }
2984 0 : delete[] s;
2985 0 : break;
2986 : }
2987 0 : case Value_Code:
2988 : {
2989 0 : char *s = cutstring(p);
2990 0 : compileblock(code, s);
2991 0 : delete[] s;
2992 0 : break;
2993 : }
2994 0 : case Value_Word:
2995 : {
2996 0 : cutstring(p, word);
2997 0 : break;
2998 : }
2999 16 : case Value_Any:
3000 : case Value_String:
3001 : {
3002 16 : compileunescapestring(code, p);
3003 16 : break;
3004 : }
3005 97 : case Value_CAny:
3006 : case Value_CString:
3007 : {
3008 97 : compileunescapestring(code, p, true);
3009 97 : break;
3010 : }
3011 0 : default:
3012 : {
3013 0 : stringslice s;
3014 0 : cutstring(p, s);
3015 0 : compileval(code, wordtype, s);
3016 0 : break;
3017 : }
3018 : }
3019 113 : return true;
3020 : }
3021 127 : case '$':
3022 : {
3023 127 : compilelookup(code, p, wordtype, prevargs);
3024 127 : return true;
3025 : }
3026 20 : case '(':
3027 20 : p++;
3028 20 : if(prevargs >= Max_Results)
3029 : {
3030 0 : code.push_back(Code_Enter);
3031 0 : compilestatements(code, p, wordtype > Value_Any ? Value_CAny : Value_Any, ')');
3032 0 : code.push_back(Code_Exit|ret_code_any(wordtype));
3033 : }
3034 : else
3035 : {
3036 20 : uint start = code.size();
3037 20 : compilestatements(code, p, wordtype > Value_Any ? Value_CAny : Value_Any, ')', prevargs);
3038 20 : if(code.size() > start)
3039 : {
3040 20 : code.push_back(Code_ResultArg|ret_code_any(wordtype));
3041 : }
3042 : else
3043 : {
3044 0 : compileval(code, wordtype);
3045 0 : return true;
3046 : }
3047 : }
3048 : switch(wordtype)
3049 : {
3050 0 : case Value_Pop:
3051 : {
3052 0 : code.push_back(Code_Pop);
3053 0 : break;
3054 : }
3055 0 : case Value_Cond:
3056 : {
3057 0 : code.push_back(Code_Cond);
3058 0 : break;
3059 : }
3060 0 : case Value_Code:
3061 : {
3062 0 : code.push_back(Code_Compile);
3063 0 : break;
3064 : }
3065 0 : case Value_Ident:
3066 : {
3067 0 : code.push_back(Code_IdentU);
3068 0 : break;
3069 : }
3070 : }
3071 20 : return true;
3072 82 : case '[':
3073 : {
3074 82 : p++;
3075 82 : compileblockmain(code, p, wordtype, prevargs);
3076 82 : return true;
3077 : }
3078 : //search for aliases
3079 4997 : default:
3080 4997 : switch(wordtype)
3081 : {
3082 445 : case Value_Pop:
3083 : {
3084 445 : const char *s = p;
3085 445 : p = parseword(p);
3086 445 : return p != s;
3087 : }
3088 41 : case Value_Cond:
3089 : {
3090 41 : char *s = cutword(p);
3091 41 : if(!s)
3092 : {
3093 14 : return false;
3094 : }
3095 27 : compileblock(code, s);
3096 27 : delete[] s;
3097 27 : return true;
3098 : }
3099 79 : case Value_Code:
3100 : {
3101 79 : char *s = cutword(p);
3102 79 : if(!s)
3103 : {
3104 56 : return false;
3105 : }
3106 23 : compileblock(code, s);
3107 23 : delete[] s;
3108 23 : return true;
3109 : }
3110 1955 : case Value_Word:
3111 1955 : cutword(p, word);
3112 1955 : return word.len!=0;
3113 2477 : default:
3114 : {
3115 2477 : stringslice s;
3116 2477 : cutword(p, s);
3117 2477 : if(!s.len)
3118 : {
3119 1409 : return false;
3120 : }
3121 1068 : compileval(code, wordtype, s);
3122 1068 : return true;
3123 : }
3124 : }
3125 : }
3126 : }
3127 :
3128 1880 : static void compilestatements(std::vector<uint> &code, const char *&p, int rettype, int brak, int prevargs)
3129 : {
3130 1880 : const char *line = p;
3131 1880 : stringslice idname;
3132 : int numargs;
3133 : for(;;)
3134 : {
3135 1955 : skipcomments(p);
3136 1955 : idname.str = nullptr;
3137 1955 : bool more = compilearg(code, p, Value_Word, prevargs, idname);
3138 1955 : if(!more)
3139 : {
3140 31 : goto endstatement;
3141 : }
3142 1924 : skipcomments(p);
3143 1924 : if(p[0] == '=')
3144 : {
3145 72 : switch(p[1])
3146 : {
3147 0 : case '/':
3148 : {
3149 0 : if(p[2] != '/')
3150 : {
3151 0 : break;
3152 : }
3153 : }
3154 : [[fallthrough]];
3155 : case ';':
3156 : case ' ':
3157 : case '\t':
3158 : case '\r':
3159 : case '\n':
3160 : case '\0':
3161 72 : p++;
3162 72 : if(idname.str)
3163 : {
3164 144 : std::string lookupsubstr = std::string(idname.str).substr(0, idname.len);
3165 72 : ident *id = newident(lookupsubstr.c_str(), Idf_Unknown);
3166 72 : if(id)
3167 : {
3168 72 : switch(id->type)
3169 : {
3170 72 : case Id_Alias:
3171 : {
3172 72 : if(!(more = compilearg(code, p, Value_Any, prevargs)))
3173 : {
3174 0 : compilestr(code);
3175 : }
3176 72 : code.push_back((id->index < Max_Args ? Code_AliasArg : Code_Alias)|(id->index<<8));
3177 72 : goto endstatement;
3178 : }
3179 0 : case Id_Var:
3180 : {
3181 0 : if(!(more = compilearg(code, p, Value_Integer, prevargs)))
3182 : {
3183 0 : compileint(code);
3184 : }
3185 0 : code.push_back(Code_IntVar1|(id->index<<8));
3186 0 : goto endstatement;
3187 : }
3188 0 : case Id_FloatVar:
3189 : {
3190 0 : if(!(more = compilearg(code, p, Value_Float, prevargs)))
3191 : {
3192 0 : compilefloat(code);
3193 : }
3194 0 : code.push_back(Code_FloatVar1|(id->index<<8));
3195 0 : goto endstatement;
3196 : }
3197 0 : case Id_StringVar:
3198 : {
3199 0 : if(!(more = compilearg(code, p, Value_CString, prevargs)))
3200 : {
3201 0 : compilestr(code);
3202 : }
3203 0 : code.push_back(Code_StrVar1|(id->index<<8));
3204 0 : goto endstatement;
3205 : }
3206 : }
3207 : }
3208 0 : compilestr(code, idname, true);
3209 72 : }
3210 0 : if(!(more = compilearg(code, p, Value_Any)))
3211 : {
3212 0 : compilestr(code);
3213 : }
3214 0 : code.push_back(Code_AliasU);
3215 0 : goto endstatement;
3216 : }
3217 : }
3218 1852 : numargs = 0;
3219 1852 : if(!idname.str)
3220 : {
3221 0 : noid:
3222 0 : while(numargs < Max_Args && (more = compilearg(code, p, Value_CAny, prevargs+numargs)))
3223 : {
3224 0 : numargs++;
3225 : }
3226 0 : code.push_back(Code_CallU|(numargs<<8));
3227 : }
3228 : else
3229 : {
3230 1852 : ident *id = nullptr;
3231 3704 : std::string lookupsubstr = std::string(idname.str).substr(0, idname.len);
3232 1852 : const auto itr = idents.find(lookupsubstr);
3233 1852 : if(itr != idents.end())
3234 : {
3235 1802 : id = &(*(itr)).second;
3236 : }
3237 1852 : if(!id)
3238 : {
3239 50 : if(!checknumber(idname.str))
3240 : {
3241 0 : compilestr(code, idname, true);
3242 0 : goto noid;
3243 : }
3244 50 : switch(rettype)
3245 : {
3246 50 : case Value_Any:
3247 : case Value_CAny:
3248 : {
3249 50 : char *end = const_cast<char *>(idname.str);
3250 50 : int val = static_cast<int>(std::strtoul(idname.str, &end, 0));
3251 50 : if(end < idname.end())
3252 : {
3253 0 : compilestr(code, idname, rettype==Value_CAny);
3254 : }
3255 : else
3256 : {
3257 50 : compileint(code, val);
3258 : }
3259 50 : break;
3260 : }
3261 0 : default:
3262 0 : compileval(code, rettype, idname);
3263 0 : break;
3264 : }
3265 50 : code.push_back(Code_Result);
3266 : }
3267 : else
3268 : {
3269 1802 : switch(id->type)
3270 : {
3271 27 : case Id_Alias:
3272 : {
3273 27 : while(numargs < Max_Args && (more = compilearg(code, p, Value_Any, prevargs+numargs)))
3274 : {
3275 0 : numargs++;
3276 : }
3277 27 : code.push_back((id->index < Max_Args ? Code_CallArg : Code_Call)|(numargs<<8)|(id->index<<13));
3278 27 : break;
3279 : }
3280 1187 : case Id_Command:
3281 : {
3282 1187 : int comtype = Code_Com,
3283 1187 : fakeargs = 0;
3284 1187 : bool rep = false;
3285 4965 : for(const char *fmt = id->args; *fmt; fmt++)
3286 : {
3287 4143 : switch(*fmt)
3288 : {
3289 647 : case 'S':
3290 : case 's':
3291 : {
3292 647 : if(more)
3293 : {
3294 542 : more = compilearg(code, p, *fmt == 's' ? Value_CString : Value_String, prevargs+numargs);
3295 : }
3296 647 : if(!more)
3297 : {
3298 354 : if(rep)
3299 : {
3300 45 : break;
3301 : }
3302 309 : compilestr(code, nullptr, 0, *fmt=='s');
3303 309 : fakeargs++;
3304 : }
3305 293 : else if(!fmt[1])
3306 : {
3307 45 : int numconc = 1;
3308 49 : while(numargs + numconc < Max_Args && (more = compilearg(code, p, Value_CString, prevargs+numargs+numconc)))
3309 : {
3310 4 : numconc++;
3311 : }
3312 45 : if(numconc > 1)
3313 : {
3314 3 : code.push_back(Code_ConC|Ret_String|(numconc<<8));
3315 : }
3316 : }
3317 602 : numargs++;
3318 602 : break;
3319 : }
3320 954 : case 'i':
3321 : {
3322 954 : if(more)
3323 : {
3324 826 : more = compilearg(code, p, Value_Integer, prevargs+numargs);
3325 : }
3326 954 : if(!more)
3327 : {
3328 434 : if(rep)
3329 : {
3330 157 : break;
3331 : }
3332 277 : compileint(code);
3333 277 : fakeargs++;
3334 : }
3335 797 : numargs++;
3336 797 : break;
3337 : }
3338 13 : case 'b':
3339 : {
3340 13 : if(more)
3341 : {
3342 4 : more = compilearg(code, p, Value_Integer, prevargs+numargs);
3343 : }
3344 13 : if(!more)
3345 : {
3346 13 : if(rep)
3347 : {
3348 0 : break;
3349 : }
3350 13 : compileint(code, INT_MIN);
3351 13 : fakeargs++;
3352 : }
3353 13 : numargs++;
3354 13 : break;
3355 : }
3356 700 : case 'f':
3357 : {
3358 700 : if(more)
3359 : {
3360 433 : more = compilearg(code, p, Value_Float, prevargs+numargs);
3361 : }
3362 700 : if(!more)
3363 : {
3364 445 : if(rep)
3365 : {
3366 72 : break;
3367 : }
3368 373 : compilefloat(code);
3369 373 : fakeargs++;
3370 : }
3371 628 : numargs++;
3372 628 : break;
3373 : }
3374 4 : case 'F':
3375 : {
3376 4 : if(more)
3377 : {
3378 0 : more = compilearg(code, p, Value_Float, prevargs+numargs);
3379 : }
3380 4 : if(!more)
3381 : {
3382 4 : if(rep)
3383 : {
3384 0 : break;
3385 : }
3386 4 : code.push_back(Code_Dup|Ret_Float);
3387 4 : fakeargs++;
3388 : }
3389 4 : numargs++;
3390 4 : break;
3391 : }
3392 91 : case 'T':
3393 : case 't':
3394 : {
3395 91 : if(more)
3396 : {
3397 46 : more = compilearg(code, p, *fmt == 't' ? Value_CAny : Value_Any, prevargs+numargs);
3398 : }
3399 91 : if(!more)
3400 : {
3401 83 : if(rep)
3402 : {
3403 0 : break;
3404 : }
3405 83 : compilenull(code);
3406 83 : fakeargs++;
3407 : }
3408 91 : numargs++;
3409 91 : break;
3410 : }
3411 0 : case 'E':
3412 : {
3413 0 : if(more)
3414 : {
3415 0 : more = compilearg(code, p, Value_Cond, prevargs+numargs);
3416 : }
3417 0 : if(!more)
3418 : {
3419 0 : if(rep)
3420 : {
3421 0 : break;
3422 : }
3423 0 : compilenull(code);
3424 0 : fakeargs++;
3425 : }
3426 0 : numargs++;
3427 0 : break;
3428 : }
3429 267 : case 'e':
3430 : {
3431 267 : if(more)
3432 : {
3433 142 : more = compilearg(code, p, Value_Code, prevargs+numargs);
3434 : }
3435 267 : if(!more)
3436 : {
3437 177 : if(rep)
3438 : {
3439 6 : break;
3440 : }
3441 171 : compileblock(code);
3442 171 : fakeargs++;
3443 : }
3444 261 : numargs++;
3445 261 : break;
3446 : }
3447 128 : case 'r':
3448 : {
3449 128 : if(more)
3450 : {
3451 117 : more = compilearg(code, p, Value_Ident, prevargs+numargs);
3452 : }
3453 128 : if(!more)
3454 : {
3455 36 : if(rep)
3456 : {
3457 0 : break;
3458 : }
3459 36 : compileident(code);
3460 36 : fakeargs++;
3461 : }
3462 128 : numargs++;
3463 128 : break;
3464 : }
3465 1 : case '$':
3466 : {
3467 1 : compileident(code, id);
3468 1 : numargs++;
3469 1 : break;
3470 : }
3471 13 : case 'N':
3472 : {
3473 13 : compileint(code, numargs-fakeargs);
3474 13 : numargs++;
3475 13 : break;
3476 : }
3477 1 : case 'D':
3478 : {
3479 1 : comtype = Code_ComD;
3480 1 : numargs++;
3481 1 : break;
3482 : }
3483 4 : case 'C':
3484 : {
3485 4 : comtype = Code_ComC;
3486 4 : if(more)
3487 : {
3488 5 : while(numargs < Max_Args && (more = compilearg(code, p, Value_CAny, prevargs+numargs)))
3489 : {
3490 1 : numargs++;
3491 : }
3492 : }
3493 4 : goto compilecomv;
3494 : }
3495 361 : case 'V':
3496 : {
3497 361 : comtype = Code_ComV;
3498 361 : if(more)
3499 : {
3500 51 : while(numargs < Max_Args && (more = compilearg(code, p, Value_CAny, prevargs+numargs)))
3501 : {
3502 32 : numargs++;
3503 : }
3504 : }
3505 361 : goto compilecomv;
3506 : }
3507 959 : case '1':
3508 : case '2':
3509 : case '3':
3510 : case '4':
3511 : {
3512 959 : if(more && numargs < Max_Args)
3513 : {
3514 617 : int numrep = *fmt-'0'+1;
3515 617 : fmt -= numrep;
3516 617 : rep = true;
3517 617 : }
3518 : else
3519 : {
3520 342 : for(; numargs > Max_Args; numargs--)
3521 : {
3522 0 : code.push_back(Code_Pop);
3523 : }
3524 : }
3525 959 : break;
3526 : }
3527 : }
3528 : }
3529 822 : code.push_back(comtype|ret_code_any(rettype)|(id->index<<8));
3530 822 : break;
3531 365 : compilecomv:
3532 365 : code.push_back(comtype|ret_code_any(rettype)|(numargs<<8)|(id->index<<13));
3533 365 : break;
3534 : }
3535 1 : case Id_Local:
3536 : {
3537 1 : if(more)
3538 : {
3539 1 : while(numargs < Max_Args && (more = compilearg(code, p, Value_Ident, prevargs+numargs)))
3540 : {
3541 0 : numargs++;
3542 : }
3543 : }
3544 1 : if(more)
3545 : {
3546 0 : while((more = compilearg(code, p, Value_Pop)))
3547 : {
3548 : //(empty body)
3549 : }
3550 : }
3551 1 : code.push_back(Code_Local|(numargs<<8));
3552 1 : break;
3553 : }
3554 1 : case Id_Do:
3555 : {
3556 1 : if(more)
3557 : {
3558 1 : more = compilearg(code, p, Value_Code, prevargs);
3559 : }
3560 1 : code.push_back((more ? Code_Do : Code_Null) | ret_code_any(rettype));
3561 1 : break;
3562 : }
3563 1 : case Id_DoArgs:
3564 : {
3565 1 : if(more)
3566 : {
3567 1 : more = compilearg(code, p, Value_Code, prevargs);
3568 : }
3569 1 : code.push_back((more ? Code_DoArgs : Code_Null) | ret_code_any(rettype));
3570 1 : break;
3571 : }
3572 8 : case Id_If:
3573 : {
3574 8 : if(more)
3575 : {
3576 8 : more = compilearg(code, p, Value_CAny, prevargs);
3577 : }
3578 8 : if(!more) //more can be affected by above assignment
3579 : {
3580 2 : code.push_back(Code_Null | ret_code_any(rettype));
3581 : }
3582 : else
3583 : {
3584 6 : int start1 = code.size();
3585 6 : more = compilearg(code, p, Value_Code, prevargs+1);
3586 6 : if(!more)
3587 : {
3588 2 : code.push_back(Code_Pop);
3589 2 : code.push_back(Code_Null | ret_code_any(rettype));
3590 : }
3591 : else
3592 : {
3593 4 : int start2 = code.size();
3594 4 : more = compilearg(code, p, Value_Code, prevargs+2);
3595 4 : uint inst1 = code[start1], op1 = inst1&~Code_RetMask, len1 = start2 - (start1+1);
3596 4 : if(!more)
3597 : {
3598 0 : if(op1 == (Code_Block|(len1<<8)))
3599 : {
3600 0 : code[start1] = (len1<<8) | Code_JumpFalse;
3601 0 : code[start1+1] = Code_EnterResult;
3602 0 : code[start1+len1] = (code[start1+len1]&~Code_RetMask) | ret_code_any(rettype);
3603 0 : break;
3604 : }
3605 0 : compileblock(code);
3606 : }
3607 : else
3608 : {
3609 4 : uint inst2 = code[start2], op2 = inst2&~Code_RetMask, len2 = code.size() - (start2+1);
3610 4 : if(op2 == (Code_Block|(len2<<8)))
3611 : {
3612 4 : if(op1 == (Code_Block|(len1<<8)))
3613 : {
3614 4 : code[start1] = ((start2-start1)<<8) | Code_JumpFalse;
3615 4 : code[start1+1] = Code_EnterResult;
3616 4 : code[start1+len1] = (code[start1+len1]&~Code_RetMask) | ret_code_any(rettype);
3617 4 : code[start2] = (len2<<8) | Code_Jump;
3618 4 : code[start2+1] = Code_EnterResult;
3619 4 : code[start2+len2] = (code[start2+len2]&~Code_RetMask) | ret_code_any(rettype);
3620 4 : break;
3621 : }
3622 0 : else if(op1 == (Code_Empty|(len1<<8)))
3623 : {
3624 0 : code[start1] = Code_Null | (inst2&Code_RetMask);
3625 0 : code[start2] = (len2<<8) | Code_JumpTrue;
3626 0 : code[start2+1] = Code_EnterResult;
3627 0 : code[start2+len2] = (code[start2+len2]&~Code_RetMask) | ret_code_any(rettype);
3628 0 : break;
3629 : }
3630 : }
3631 : }
3632 0 : code.push_back(Code_Com|ret_code_any(rettype)|(id->index<<8));
3633 : }
3634 : }
3635 4 : break;
3636 : }
3637 53 : case Id_Result:
3638 : {
3639 53 : if(more)
3640 : {
3641 53 : more = compilearg(code, p, Value_Any, prevargs);
3642 : }
3643 53 : code.push_back((more ? Code_Result : Code_Null) | ret_code_any(rettype));
3644 53 : break;
3645 : }
3646 7 : case Id_Not:
3647 : {
3648 7 : if(more)
3649 : {
3650 7 : more = compilearg(code, p, Value_CAny, prevargs);
3651 : }
3652 7 : code.push_back((more ? Code_Not : Code_True) | ret_code_any(rettype));
3653 7 : break;
3654 : }
3655 14 : case Id_And:
3656 : case Id_Or:
3657 : {
3658 14 : if(more)
3659 : {
3660 14 : more = compilearg(code, p, Value_Cond, prevargs);
3661 : }
3662 14 : if(!more) //more can be affected by above assignment
3663 : {
3664 4 : code.push_back((id->type == Id_And ? Code_True : Code_False) | ret_code_any(rettype));
3665 : }
3666 : else
3667 : {
3668 10 : numargs++;
3669 10 : int start = code.size(),
3670 10 : end = start;
3671 27 : while(numargs < Max_Args)
3672 : {
3673 27 : more = compilearg(code, p, Value_Cond, prevargs+numargs);
3674 27 : if(!more)
3675 : {
3676 10 : break;
3677 : }
3678 17 : numargs++;
3679 17 : if((code[end]&~Code_RetMask) != (Code_Block|(static_cast<uint>(code.size()-(end+1))<<8)))
3680 : {
3681 0 : break;
3682 : }
3683 17 : end = code.size();
3684 : }
3685 10 : if(more)
3686 : {
3687 0 : while(numargs < Max_Args && (more = compilearg(code, p, Value_Cond, prevargs+numargs)))
3688 : {
3689 0 : numargs++;
3690 : }
3691 0 : code.push_back(Code_ComV|ret_code_any(rettype)|(numargs<<8)|(id->index<<13));
3692 : }
3693 : else
3694 : {
3695 10 : uint op = id->type == Id_And ? Code_JumpResultFalse : Code_JumpResultTrue;
3696 10 : code.push_back(op);
3697 10 : end = code.size();
3698 27 : while(start+1 < end)
3699 : {
3700 17 : uint len = code[start]>>8;
3701 17 : code[start] = ((end-(start+1))<<8) | op;
3702 17 : code[start+1] = Code_Enter;
3703 17 : code[start+len] = (code[start+len]&~Code_RetMask) | ret_code_any(rettype);
3704 17 : start += len+1;
3705 : }
3706 : }
3707 : }
3708 14 : break;
3709 : }
3710 387 : case Id_Var:
3711 : {
3712 387 : if(!(more = compilearg(code, p, Value_Integer, prevargs)))
3713 : {
3714 387 : code.push_back(Code_Print|(id->index<<8));
3715 : }
3716 0 : else if(!(id->flags&Idf_Hex) || !(more = compilearg(code, p, Value_Integer, prevargs+1)))
3717 : {
3718 0 : code.push_back(Code_IntVar1|(id->index<<8));
3719 : }
3720 0 : else if(!(more = compilearg(code, p, Value_Integer, prevargs+2)))
3721 : {
3722 0 : code.push_back(Code_IntVar2|(id->index<<8));
3723 : }
3724 : else
3725 : {
3726 0 : code.push_back(Code_IntVar3|(id->index<<8));
3727 : }
3728 387 : break;
3729 : }
3730 112 : case Id_FloatVar:
3731 : {
3732 112 : if(!(more = compilearg(code, p, Value_Float, prevargs)))
3733 : {
3734 112 : code.push_back(Code_Print|(id->index<<8));
3735 : }
3736 : else
3737 : {
3738 0 : code.push_back(Code_FloatVar1|(id->index<<8));
3739 : }
3740 112 : break;
3741 : }
3742 4 : case Id_StringVar:
3743 : {
3744 4 : if(!(more = compilearg(code, p, Value_CString, prevargs)))
3745 : {
3746 4 : code.push_back(Code_Print|(id->index<<8));
3747 : }
3748 : else
3749 : {
3750 : do
3751 : {
3752 0 : ++numargs;
3753 0 : } while(numargs < Max_Args && (more = compilearg(code, p, Value_CAny, prevargs+numargs)));
3754 0 : if(numargs > 1)
3755 : {
3756 0 : code.push_back(Code_ConC|Ret_String|(numargs<<8));
3757 : }
3758 0 : code.push_back(Code_StrVar1|(id->index<<8));
3759 : }
3760 4 : break;
3761 : }
3762 : }
3763 : }
3764 1852 : }
3765 1955 : endstatement:
3766 1955 : if(more)
3767 : {
3768 445 : while(compilearg(code, p, Value_Pop))
3769 : {
3770 : //(empty body)
3771 : }
3772 : }
3773 1955 : p += std::strcspn(p, ")];/\n\0");
3774 1955 : int c = *p++;
3775 1955 : switch(c)
3776 : {
3777 1786 : case '\0':
3778 : {
3779 1786 : if(c != brak)
3780 : {
3781 0 : debugcodeline(line, "missing \"%c\"", brak);
3782 : }
3783 1786 : p--;
3784 1786 : return;
3785 : }
3786 94 : case ')':
3787 : case ']':
3788 : {
3789 94 : if(c == brak)
3790 : {
3791 94 : return;
3792 : }
3793 0 : debugcodeline(line, "unexpected \"%c\"", c);
3794 0 : break;
3795 : }
3796 0 : case '/':
3797 : {
3798 0 : if(*p == '/')
3799 : {
3800 0 : p += std::strcspn(p, "\n\0");
3801 : }
3802 0 : goto endstatement;
3803 : }
3804 : }
3805 75 : }
3806 : }
3807 :
3808 1736 : static void compilemain(std::vector<uint> &code, const char *p, int rettype = Value_Any)
3809 : {
3810 1736 : code.push_back(Code_Start);
3811 1736 : compilestatements(code, p, Value_Any);
3812 1736 : code.push_back(Code_Exit|(rettype < Value_Any ? rettype<<Code_Ret : 0));
3813 1736 : }
3814 :
3815 28 : uint *compilecode(const char *p)
3816 : {
3817 28 : std::vector<uint> buf;
3818 28 : buf.reserve(64);
3819 28 : compilemain(buf, p);
3820 28 : uint *code = new uint[buf.size()];
3821 28 : std::memcpy(code, buf.data(), buf.size()*sizeof(uint));
3822 28 : code[0] += 0x100;
3823 28 : return code;
3824 28 : }
3825 :
3826 0 : static const uint *forcecode(tagval &v)
3827 : {
3828 0 : if(v.type != Value_Code)
3829 : {
3830 0 : std::vector<uint> buf;
3831 0 : buf.reserve(64);
3832 0 : compilemain(buf, v.getstr());
3833 0 : freearg(v);
3834 0 : uint * arr = new uint[buf.size()];
3835 0 : std::memcpy(arr, buf.data(), buf.size()*sizeof(uint));
3836 0 : v.setcode(arr+1);
3837 0 : }
3838 0 : return v.code;
3839 : }
3840 :
3841 0 : static void forcecond(tagval &v)
3842 : {
3843 0 : switch(v.type)
3844 : {
3845 0 : case Value_String:
3846 : case Value_Macro:
3847 : case Value_CString:
3848 : {
3849 0 : if(v.s[0])
3850 : {
3851 0 : forcecode(v);
3852 : }
3853 : else
3854 : {
3855 0 : v.setint(0);
3856 : }
3857 0 : break;
3858 : }
3859 : }
3860 0 : }
3861 :
3862 0 : void freecode(uint *code)
3863 : {
3864 0 : if(!code)
3865 : {
3866 0 : return;
3867 : }
3868 0 : switch(*code&Code_OpMask)
3869 : {
3870 0 : case Code_Start:
3871 : {
3872 0 : *code -= 0x100;
3873 0 : if(static_cast<int>(*code) < 0x100)
3874 : {
3875 0 : delete[] code;
3876 : }
3877 0 : return;
3878 : }
3879 : }
3880 0 : switch(code[-1]&Code_OpMask)
3881 : {
3882 0 : case Code_Start:
3883 : {
3884 0 : code[-1] -= 0x100;
3885 0 : if(static_cast<int>(code[-1]) < 0x100)
3886 : {
3887 0 : delete[] &code[-1];
3888 : }
3889 0 : break;
3890 : }
3891 0 : case Code_Offset:
3892 : {
3893 0 : code -= static_cast<int>(code[-1]>>8);
3894 0 : *code -= 0x100;
3895 0 : if(static_cast<int>(*code) < 0x100)
3896 : {
3897 0 : delete[] code;
3898 : }
3899 0 : break;
3900 : }
3901 : }
3902 : }
3903 :
3904 388 : void printvar(const ident *id, int i)
3905 : {
3906 388 : if(i < 0)
3907 : {
3908 4 : conoutf("%s = %d", id->name, i);
3909 : }
3910 384 : else if(id->flags&Idf_Hex && id->maxval==0xFFFFFF)
3911 : {
3912 45 : conoutf("%s = 0x%.6X (%d, %d, %d)", id->name, i, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
3913 : }
3914 : else
3915 : {
3916 339 : conoutf(id->flags&Idf_Hex ? "%s = 0x%X" : "%s = %d", id->name, i);
3917 : }
3918 388 : }
3919 :
3920 112 : void printfvar(const ident *id, float f)
3921 : {
3922 112 : conoutf("%s = %s", id->name, floatstr(f));
3923 112 : }
3924 :
3925 4 : void printsvar(const ident *id, const char *s)
3926 : {
3927 4 : conoutf(std::strchr(s, '"') ? "%s = [%s]" : "%s = \"%s\"", id->name, s);
3928 4 : }
3929 :
3930 503 : void printvar(const ident *id)
3931 : {
3932 503 : switch(id->type)
3933 : {
3934 387 : case Id_Var:
3935 : {
3936 387 : printvar(id, *id->storage.i);
3937 387 : break;
3938 : }
3939 112 : case Id_FloatVar:
3940 : {
3941 112 : printfvar(id, *id->storage.f);
3942 112 : break;
3943 : }
3944 4 : case Id_StringVar:
3945 : {
3946 4 : printsvar(id, *id->storage.s);
3947 4 : break;
3948 : }
3949 : }
3950 503 : }
3951 : //You know what they awoke in the darkness of Khazad-dum... shadow and flame.
3952 : // these are typedefs for argument lists of variable sizes
3953 :
3954 : // they will be used below to typecast various id->fun objects to different lengths
3955 : // which allows them to only accept certain lengths of arguments
3956 : // up to 12 args typedef'd here, could be extended with more typedefs (buy why?)
3957 :
3958 : //comfun stands for COMmand FUNction
3959 : typedef void (__cdecl *comfun)();
3960 : typedef void (__cdecl *comfun1)(void *);
3961 : typedef void (__cdecl *comfun2)(void *, void *);
3962 : typedef void (__cdecl *comfun3)(void *, void *, void *);
3963 : typedef void (__cdecl *comfun4)(void *, void *, void *, void *);
3964 : typedef void (__cdecl *comfun5)(void *, void *, void *, void *, void *);
3965 : typedef void (__cdecl *comfun6)(void *, void *, void *, void *, void *, void *);
3966 : typedef void (__cdecl *comfun7)(void *, void *, void *, void *, void *, void *, void *);
3967 : typedef void (__cdecl *comfun8)(void *, void *, void *, void *, void *, void *, void *, void *);
3968 : typedef void (__cdecl *comfun9)(void *, void *, void *, void *, void *, void *, void *, void *, void *);
3969 : typedef void (__cdecl *comfun10)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
3970 : typedef void (__cdecl *comfun11)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
3971 : typedef void (__cdecl *comfun12)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
3972 : typedef void (__cdecl *comfunv)(tagval *, int);
3973 :
3974 0 : static const uint *skipcode(const uint *code, tagval &result = noret)
3975 : {
3976 0 : int depth = 0;
3977 : for(;;)
3978 : {
3979 0 : uint op = *code++;
3980 0 : switch(op&0xFF)
3981 : {
3982 0 : case Code_Macro:
3983 : case Code_Val|Ret_String:
3984 : {
3985 0 : uint len = op>>8;
3986 0 : code += len/sizeof(uint) + 1;
3987 0 : continue;
3988 0 : }
3989 0 : case Code_Block:
3990 : case Code_Jump:
3991 : case Code_JumpTrue:
3992 : case Code_JumpFalse:
3993 : case Code_JumpResultTrue:
3994 : case Code_JumpResultFalse:
3995 : {
3996 0 : uint len = op>>8;
3997 0 : code += len;
3998 0 : continue;
3999 0 : }
4000 0 : case Code_Enter:
4001 : case Code_EnterResult:
4002 : {
4003 0 : ++depth;
4004 0 : continue;
4005 : }
4006 0 : case Code_Exit|Ret_Null:
4007 : case Code_Exit|Ret_String:
4008 : case Code_Exit|Ret_Integer:
4009 : case Code_Exit|Ret_Float:
4010 : {
4011 0 : if(depth <= 0)
4012 : {
4013 0 : if(&result != &noret)
4014 : {
4015 0 : forcearg(result, op&Code_RetMask);
4016 : }
4017 0 : return code;
4018 : }
4019 0 : --depth;
4020 0 : continue;
4021 : }
4022 0 : }
4023 0 : }
4024 : }
4025 :
4026 0 : static uint *copycode(const uint *src)
4027 : {
4028 0 : const uint *end = skipcode(src);
4029 0 : size_t len = end - src;
4030 0 : uint *dst = new uint[len + 1];
4031 0 : *dst++ = Code_Start;
4032 0 : std::memcpy(dst, src, len*sizeof(uint));
4033 0 : return dst;
4034 : }
4035 :
4036 0 : static void copyarg(tagval &dst, const tagval &src)
4037 : {
4038 0 : switch(src.type)
4039 : {
4040 0 : case Value_Integer:
4041 : case Value_Float:
4042 : case Value_Ident:
4043 : {
4044 0 : dst = src;
4045 0 : break;
4046 : }
4047 0 : case Value_String:
4048 : case Value_Macro:
4049 : case Value_CString:
4050 : {
4051 0 : dst.setstr(newstring(src.s));
4052 0 : break;
4053 : }
4054 0 : case Value_Code:
4055 : {
4056 0 : dst.setcode(copycode(src.code));
4057 0 : break;
4058 : }
4059 0 : default:
4060 : {
4061 0 : dst.setnull();
4062 0 : break;
4063 : }
4064 : }
4065 0 : }
4066 :
4067 1 : static void addreleaseaction(ident *id, tagval *args, int numargs)
4068 : {
4069 1 : tagval *dst = addreleaseaction(id, numargs+1);
4070 1 : if(dst)
4071 : {
4072 0 : args[numargs].setint(1);
4073 0 : for(int i = 0; i < numargs+1; ++i)
4074 : {
4075 0 : copyarg(dst[i], args[i]);
4076 : }
4077 : }
4078 : else
4079 : {
4080 1 : args[numargs].setint(0);
4081 : }
4082 1 : }
4083 :
4084 : /**
4085 : * @brief Returns the pointer to a string or integer argument.
4086 : * @param id the identifier, whether a string or integer.
4087 : * @param args the array of arguments.
4088 : * @param n the n-th argument to return.
4089 : * @param offset the offset for accessing and returning the n-th argument.
4090 : * @return void* the pointer to the string or integer.
4091 : */
4092 1886 : void* arg(const ident *id, tagval args[], int n, int offset = 0)
4093 : {
4094 1886 : if(id->argmask&(1<<n))
4095 : {
4096 884 : return reinterpret_cast<void *>(args[n + offset].s);
4097 : }
4098 1002 : return reinterpret_cast<void *>(&args[n + offset].i);
4099 : }
4100 :
4101 : /**
4102 : * @brief Takes a number `n` and type-mangles the id->fun field to
4103 : * whatever length function is desired. E.g. callcom(id, args, 6) takes the function
4104 : * pointer id->fun and changes its type to comfun6 (command function w/ 6 args).
4105 : * Each argument is then described by the arg() function for each argument slot.
4106 : *
4107 : * the id->fun member is a pointer to a free function with the signature
4108 : * void foo(ident *), which is then reinterpret_casted to a function with parameters
4109 : * that it originally had before being stored as a generic function pointer.
4110 : *
4111 : * @param id the identifier for the type of command.
4112 : * @param args the command arguments.
4113 : * @param n the n-th argument to return.
4114 : * @param offset the offset for accessing and returning the n-th argument.
4115 : */
4116 841 : void callcom(const ident *id, tagval args[], int n, int offset=0)
4117 : {
4118 : /**
4119 : * @brief Return the n-th argument. The lambda expression captures the `id`
4120 : * and `args` so only the parameter number `n` needs to be passed.
4121 : */
4122 1886 : auto a = [id, args, offset](int n)
4123 : {
4124 1886 : return arg(id, args, n, offset);
4125 841 : };
4126 :
4127 841 : switch(n)
4128 : {
4129 69 : case 0: reinterpret_cast<comfun>(id->fun)(); break;
4130 248 : case 1: reinterpret_cast<comfun1>(id->fun)(a(0)); break;
4131 221 : case 2: reinterpret_cast<comfun2>(id->fun)(a(0), a(1)); break;
4132 124 : case 3: reinterpret_cast<comfun3>(id->fun)(a(0), a(1), a(2)); break;
4133 107 : case 4: reinterpret_cast<comfun4>(id->fun)(a(0), a(1), a(2), a(3)); break;
4134 51 : case 5: reinterpret_cast<comfun5>(id->fun)(a(0), a(1), a(2), a(3), a(4)); break;
4135 10 : case 6: reinterpret_cast<comfun6>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5)); break;
4136 7 : case 7: reinterpret_cast<comfun7>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6)); break;
4137 4 : case 8: reinterpret_cast<comfun8>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7)); break;
4138 0 : case 9: reinterpret_cast<comfun9>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8)); break;
4139 0 : case 10: reinterpret_cast<comfun10>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8), a(9)); break;
4140 0 : case 11: reinterpret_cast<comfun11>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8), a(9), a(10)); break;
4141 0 : case 12: reinterpret_cast<comfun12>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8), a(9), a(10), a(11)); break;
4142 : }
4143 841 : }
4144 :
4145 1 : static void callcommand(ident *id, tagval *args, int numargs, bool lookup = false)
4146 : {
4147 1 : int i = -1,
4148 1 : fakeargs = 0;
4149 1 : bool rep = false;
4150 1 : for(const char *fmt = id->args; *fmt; fmt++)
4151 : {
4152 0 : switch(*fmt)
4153 : {
4154 0 : case 'i':
4155 : {
4156 0 : if(++i >= numargs)
4157 : {
4158 0 : if(rep)
4159 : {
4160 0 : break;
4161 : }
4162 0 : args[i].setint(0);
4163 0 : fakeargs++;
4164 : }
4165 : else
4166 : {
4167 0 : forceint(args[i]);
4168 : }
4169 0 : break;
4170 : }
4171 0 : case 'b':
4172 : {
4173 0 : if(++i >= numargs)
4174 : {
4175 0 : if(rep)
4176 : {
4177 0 : break;
4178 : }
4179 0 : args[i].setint(INT_MIN);
4180 0 : fakeargs++;
4181 : }
4182 : else
4183 : {
4184 0 : forceint(args[i]);
4185 : }
4186 0 : break;
4187 : }
4188 0 : case 'f':
4189 : {
4190 0 : if(++i >= numargs)
4191 : {
4192 0 : if(rep)
4193 : {
4194 0 : break;
4195 : }
4196 0 : args[i].setfloat(0.0f);
4197 0 : fakeargs++;
4198 : }
4199 : else
4200 : {
4201 0 : forcefloat(args[i]);
4202 : }
4203 0 : break;
4204 : }
4205 : [[fallthrough]];
4206 0 : case 'F':
4207 : {
4208 0 : if(++i >= numargs)
4209 : {
4210 0 : if(rep)
4211 : {
4212 0 : break;
4213 : }
4214 0 : args[i].setfloat(args[i-1].getfloat());
4215 0 : fakeargs++;
4216 : }
4217 : else
4218 : {
4219 0 : forcefloat(args[i]);
4220 : }
4221 0 : break;
4222 : }
4223 0 : case 'S':
4224 : {
4225 0 : if(++i >= numargs)
4226 : {
4227 0 : if(rep)
4228 : {
4229 0 : break;
4230 : }
4231 0 : args[i].setstr(newstring(""));
4232 0 : fakeargs++;
4233 : }
4234 : else
4235 : {
4236 0 : forcestr(args[i]);
4237 : }
4238 0 : break;
4239 : }
4240 0 : case 's':
4241 : {
4242 0 : if(++i >= numargs)
4243 : {
4244 0 : if(rep)
4245 : {
4246 0 : break;
4247 : }
4248 0 : args[i].setcstr("");
4249 0 : fakeargs++;
4250 : }
4251 : else
4252 : {
4253 0 : forcestr(args[i]);
4254 : }
4255 0 : break;
4256 : }
4257 0 : case 'T':
4258 : case 't':
4259 : {
4260 0 : if(++i >= numargs)
4261 : {
4262 0 : if(rep)
4263 : {
4264 0 : break;
4265 : }
4266 0 : args[i].setnull();
4267 0 : fakeargs++;
4268 : }
4269 0 : break;
4270 : }
4271 0 : case 'E':
4272 : {
4273 0 : if(++i >= numargs)
4274 : {
4275 0 : if(rep)
4276 : {
4277 0 : break;
4278 : }
4279 0 : args[i].setnull();
4280 0 : fakeargs++;
4281 : }
4282 : else
4283 : {
4284 0 : forcecond(args[i]);
4285 : }
4286 0 : break;
4287 : }
4288 0 : case 'e':
4289 : {
4290 0 : if(++i >= numargs)
4291 : {
4292 0 : if(rep)
4293 : {
4294 0 : break;
4295 : }
4296 0 : args[i].setcode(emptyblock[Value_Null]+1);
4297 0 : fakeargs++;
4298 : }
4299 : else
4300 : {
4301 0 : forcecode(args[i]);
4302 : }
4303 0 : break;
4304 : }
4305 0 : case 'r':
4306 : {
4307 0 : if(++i >= numargs)
4308 : {
4309 0 : if(rep)
4310 : {
4311 0 : break;
4312 : }
4313 0 : args[i].setident(dummyident);
4314 0 : fakeargs++;
4315 : }
4316 : else
4317 : {
4318 0 : forceident(args[i]);
4319 0 : break;
4320 : }
4321 : }
4322 : case '$':
4323 : {
4324 0 : if(++i < numargs)
4325 : {
4326 0 : freearg(args[i]);
4327 : }
4328 0 : args[i].setident(id);
4329 0 : break;
4330 : }
4331 0 : case 'N':
4332 : {
4333 0 : if(++i < numargs)
4334 : {
4335 0 : freearg(args[i]);
4336 : }
4337 0 : args[i].setint(lookup ? -1 : i-fakeargs);
4338 0 : break;
4339 : }
4340 0 : case 'D':
4341 : {
4342 0 : if(++i < numargs)
4343 : {
4344 0 : freearg(args[i]);
4345 : }
4346 0 : addreleaseaction(id, args, i);
4347 0 : fakeargs++;
4348 0 : break;
4349 : }
4350 0 : case 'C':
4351 : {
4352 0 : i = std::max(i+1, numargs);
4353 0 : std::vector<char> buf;
4354 0 : reinterpret_cast<comfun1>(id->fun)(conc(buf, args, i, true));
4355 0 : goto cleanup;
4356 0 : }
4357 0 : case 'V':
4358 : {
4359 0 : i = std::max(i+1, numargs);
4360 0 : reinterpret_cast<comfunv>(id->fun)(args, i);
4361 0 : goto cleanup;
4362 : }
4363 0 : case '1':
4364 : case '2':
4365 : case '3':
4366 : case '4':
4367 : {
4368 0 : if(i+1 < numargs)
4369 : {
4370 0 : fmt -= *fmt-'0'+1;
4371 0 : rep = true;
4372 : }
4373 0 : break;
4374 : }
4375 : }
4376 : }
4377 1 : ++i;
4378 1 : callcom(id, args, i);
4379 :
4380 1 : cleanup:
4381 1 : for(int k = 0; k < i; ++k)
4382 : {
4383 0 : freearg(args[k]);
4384 : }
4385 1 : for(; i < numargs; i++)
4386 : {
4387 0 : freearg(args[i]);
4388 : }
4389 1 : }
4390 :
4391 : static constexpr int maxrundepth = 255; //limit for rundepth (nesting depth) var below
4392 : static int rundepth = 0; //current rundepth
4393 :
4394 : #define UNDOARGS \
4395 : identstack argstack[Max_Args]; \
4396 : IdentLink *prevstack = aliasstack; \
4397 : IdentLink aliaslink; \
4398 : for(int undos = 0; prevstack != &noalias; prevstack = prevstack->next) \
4399 : { \
4400 : if(prevstack->usedargs & undoflag) \
4401 : { \
4402 : ++undos; \
4403 : } \
4404 : else if(undos > 0) \
4405 : { \
4406 : --undos; \
4407 : } \
4408 : else \
4409 : { \
4410 : prevstack = prevstack->next; \
4411 : for(int argmask = aliasstack->usedargs & ~undoflag, i = 0; argmask; argmask >>= 1, i++) \
4412 : { \
4413 : if(argmask&1) \
4414 : { \
4415 : undoarg(*identmap[i], argstack[i]); \
4416 : } \
4417 : } \
4418 : aliaslink.id = aliasstack->id; \
4419 : aliaslink.next = aliasstack; \
4420 : aliaslink.usedargs = undoflag | prevstack->usedargs; \
4421 : aliaslink.argstack = prevstack->argstack; \
4422 : aliasstack = &aliaslink; \
4423 : break; \
4424 : } \
4425 : } \
4426 :
4427 :
4428 : #define REDOARGS \
4429 : if(aliasstack == &aliaslink) \
4430 : { \
4431 : prevstack->usedargs |= aliaslink.usedargs & ~undoflag; \
4432 : aliasstack = aliaslink.next; \
4433 : for(int argmask = aliasstack->usedargs & ~undoflag, i = 0; argmask; argmask >>= 1, i++) \
4434 : { \
4435 : if(argmask&1) \
4436 : { \
4437 : redoarg(*identmap[i], argstack[i]); \
4438 : } \
4439 : } \
4440 : }
4441 :
4442 2173 : static const uint *runcode(const uint *code, tagval &result)
4443 : {
4444 2173 : result.setnull();
4445 2173 : if(rundepth >= maxrundepth)
4446 : {
4447 0 : debugcode("exceeded recursion limit");
4448 0 : return skipcode(code, result);
4449 : }
4450 2173 : ++rundepth;
4451 2173 : int numargs = 0;
4452 : tagval args[Max_Args+Max_Results],
4453 2173 : *prevret = commandret;
4454 2173 : commandret = &result;
4455 : for(;;)
4456 : {
4457 7628 : uint op = *code++;
4458 7628 : switch(op&0xFF)
4459 : {
4460 0 : case Code_Start:
4461 : case Code_Offset:
4462 : {
4463 0 : continue;
4464 : }
4465 : // For Code_Null cases, set results to null, empty, or 0 values.
4466 13 : case Code_Null|Ret_Null:
4467 : {
4468 13 : freearg(result);
4469 13 : result.setnull();
4470 13 : continue;
4471 : }
4472 0 : case Code_Null|Ret_String:
4473 : {
4474 0 : freearg(result);
4475 0 : result.setstr(newstring(""));
4476 0 : continue;
4477 : }
4478 0 : case Code_Null|Ret_Integer:
4479 : {
4480 0 : freearg(result);
4481 0 : result.setint(0);
4482 0 : continue;
4483 : }
4484 0 : case Code_Null|Ret_Float:
4485 : {
4486 0 : freearg(result);
4487 0 : result.setfloat(0.0f);
4488 0 : continue;
4489 : }
4490 : // For Code_False cases, set results to 0 values.
4491 0 : case Code_False|Ret_String:
4492 : {
4493 0 : freearg(result);
4494 0 : result.setstr(newstring("0"));
4495 0 : continue;
4496 : }
4497 2 : case Code_False|Ret_Null: // Null case left empty intentionally.
4498 : case Code_False|Ret_Integer:
4499 : {
4500 2 : freearg(result);
4501 2 : result.setint(0);
4502 2 : continue;
4503 : }
4504 0 : case Code_False|Ret_Float:
4505 : {
4506 0 : freearg(result);
4507 0 : result.setfloat(0.0f);
4508 0 : continue;
4509 : }
4510 : // For Code_False cases, set results to 1 values.
4511 0 : case Code_True|Ret_String:
4512 : {
4513 0 : freearg(result);
4514 0 : result.setstr(newstring("1"));
4515 0 : continue;
4516 : }
4517 3 : case Code_True|Ret_Null: // Null case left empty intentionally.
4518 : case Code_True|Ret_Integer:
4519 : {
4520 3 : freearg(result);
4521 3 : result.setint(1);
4522 3 : continue;
4523 : }
4524 0 : case Code_True|Ret_Float:
4525 : {
4526 0 : freearg(result);
4527 0 : result.setfloat(1.0f);
4528 0 : continue;
4529 : }
4530 : // For Code_Not cases, negate values (flip 0's and 1's).
4531 0 : case Code_Not|Ret_String:
4532 : {
4533 0 : freearg(result);
4534 0 : --numargs;
4535 0 : result.setstr(newstring(getbool(args[numargs]) ? "0" : "1"));
4536 0 : freearg(args[numargs]);
4537 0 : continue;
4538 : }
4539 6 : case Code_Not|Ret_Null: // Null case left empty intentionally.
4540 : case Code_Not|Ret_Integer:
4541 : {
4542 6 : freearg(result);
4543 6 : --numargs;
4544 6 : result.setint(getbool(args[numargs]) ? 0 : 1);
4545 6 : freearg(args[numargs]);
4546 6 : continue;
4547 : }
4548 0 : case Code_Not|Ret_Float:
4549 : {
4550 0 : freearg(result);
4551 0 : --numargs;
4552 0 : result.setfloat(getbool(args[numargs]) ? 0.0f : 1.0f);
4553 0 : freearg(args[numargs]);
4554 0 : continue;
4555 : }
4556 2 : case Code_Pop:
4557 : {
4558 2 : freearg(args[--numargs]);
4559 2 : continue;
4560 : }
4561 9 : case Code_Enter:
4562 : {
4563 9 : code = runcode(code, args[numargs++]);
4564 9 : continue;
4565 : }
4566 4 : case Code_EnterResult:
4567 : {
4568 4 : freearg(result);
4569 4 : code = runcode(code, result);
4570 4 : continue;
4571 : }
4572 1432 : case Code_Exit|Ret_String:
4573 : case Code_Exit|Ret_Integer:
4574 : case Code_Exit|Ret_Float:
4575 : {
4576 1432 : forcearg(result, op&Code_RetMask);
4577 : }
4578 : [[fallthrough]];
4579 2172 : case Code_Exit|Ret_Null:
4580 : {
4581 2172 : goto exit;
4582 : }
4583 0 : case Code_ResultArg|Ret_String:
4584 : case Code_ResultArg|Ret_Integer:
4585 : case Code_ResultArg|Ret_Float:
4586 : {
4587 0 : forcearg(result, op&Code_RetMask);
4588 : }
4589 : [[fallthrough]];
4590 108 : case Code_ResultArg|Ret_Null:
4591 : {
4592 108 : args[numargs++] = result;
4593 108 : result.setnull();
4594 108 : continue;
4595 : }
4596 503 : case Code_Print:
4597 : {
4598 503 : printvar(identmap[op>>8]);
4599 503 : continue;
4600 : }
4601 1 : case Code_Local:
4602 : {
4603 1 : freearg(result);
4604 1 : int numlocals = op>>8,
4605 1 : offset = numargs-numlocals;
4606 : identstack locals[Max_Args];
4607 1 : for(int i = 0; i < numlocals; ++i)
4608 : {
4609 0 : pushalias(*args[offset+i].id, locals[i]);
4610 : }
4611 1 : code = runcode(code, result);
4612 1 : for(int i = offset; i < numargs; i++)
4613 : {
4614 0 : popalias(*args[i].id);
4615 : }
4616 1 : goto exit;
4617 : }
4618 0 : case Code_DoArgs|Ret_Null:
4619 : case Code_DoArgs|Ret_String:
4620 : case Code_DoArgs|Ret_Integer:
4621 : case Code_DoArgs|Ret_Float:
4622 : {
4623 0 : UNDOARGS
4624 0 : freearg(result);
4625 0 : runcode(args[--numargs].code, result);
4626 0 : freearg(args[numargs]);
4627 0 : forcearg(result, op&Code_RetMask);
4628 0 : REDOARGS
4629 0 : continue;
4630 0 : }
4631 0 : case Code_Do|Ret_Null:
4632 : case Code_Do|Ret_String:
4633 : case Code_Do|Ret_Integer:
4634 : case Code_Do|Ret_Float:
4635 : {
4636 0 : freearg(result);
4637 0 : runcode(args[--numargs].code, result);
4638 0 : freearg(args[numargs]);
4639 0 : forcearg(result, op&Code_RetMask);
4640 0 : continue;
4641 : }
4642 2 : case Code_Jump:
4643 : {
4644 2 : uint len = op>>8;
4645 2 : code += len;
4646 2 : continue;
4647 2 : }
4648 0 : case Code_JumpTrue:
4649 : {
4650 0 : uint len = op>>8;
4651 0 : if(getbool(args[--numargs]))
4652 : {
4653 0 : code += len;
4654 : }
4655 0 : freearg(args[numargs]);
4656 0 : continue;
4657 0 : }
4658 4 : case Code_JumpFalse:
4659 : {
4660 4 : uint len = op>>8;
4661 4 : if(!getbool(args[--numargs]))
4662 : {
4663 2 : code += len;
4664 : }
4665 4 : freearg(args[numargs]);
4666 4 : continue;
4667 4 : }
4668 7 : case Code_JumpResultTrue:
4669 : {
4670 7 : uint len = op>>8;
4671 7 : freearg(result);
4672 7 : --numargs;
4673 7 : if(args[numargs].type == Value_Code)
4674 : {
4675 6 : runcode(args[numargs].code, result);
4676 6 : freearg(args[numargs]);
4677 : }
4678 : else
4679 : {
4680 1 : result = args[numargs];
4681 : }
4682 7 : if(getbool(result))
4683 : {
4684 4 : code += len;
4685 : }
4686 7 : continue;
4687 7 : }
4688 12 : case Code_JumpResultFalse:
4689 : {
4690 12 : uint len = op>>8;
4691 12 : freearg(result);
4692 12 : --numargs;
4693 12 : if(args[numargs].type == Value_Code)
4694 : {
4695 4 : runcode(args[numargs].code, result);
4696 4 : freearg(args[numargs]);
4697 : }
4698 : else
4699 : {
4700 8 : result = args[numargs];
4701 : }
4702 12 : if(!getbool(result))
4703 : {
4704 1 : code += len;
4705 : }
4706 12 : continue;
4707 12 : }
4708 627 : case Code_Macro:
4709 : {
4710 627 : uint len = op>>8;
4711 627 : args[numargs++].setmacro(code);
4712 627 : code += len/sizeof(uint) + 1;
4713 627 : continue;
4714 627 : }
4715 20 : case Code_Val|Ret_String:
4716 : {
4717 20 : uint len = op>>8;
4718 20 : const char * codearr = reinterpret_cast<const char *>(code);
4719 : //char * str = newstring(codearr, len);
4720 : //copystring(new char[len+1], codearr, len+1);
4721 20 : char * str = new char[len+1];
4722 20 : std::memcpy(str, codearr, len*sizeof(uchar));
4723 20 : str[len] = 0;
4724 :
4725 20 : args[numargs++].setstr(str);
4726 20 : code += len/sizeof(uint) + 1;
4727 20 : continue;
4728 20 : }
4729 67 : case Code_ValI|Ret_String:
4730 : {
4731 67 : char s[4] = { static_cast<char>((op>>8)&0xFF), static_cast<char>((op>>16)&0xFF), static_cast<char>((op>>24)&0xFF), '\0' };
4732 67 : args[numargs++].setstr(newstring(s));
4733 67 : continue;
4734 67 : }
4735 83 : case Code_Val|Ret_Null:
4736 : case Code_ValI|Ret_Null:
4737 : {
4738 83 : args[numargs++].setnull();
4739 83 : continue;
4740 : }
4741 17 : case Code_Val|Ret_Integer:
4742 : {
4743 17 : args[numargs++].setint(static_cast<int>(*code++));
4744 17 : continue;
4745 : }
4746 846 : case Code_ValI|Ret_Integer:
4747 : {
4748 846 : args[numargs++].setint(static_cast<int>(op)>>8);
4749 846 : continue;
4750 : }
4751 32 : case Code_Val|Ret_Float:
4752 : {
4753 32 : args[numargs++].setfloat(*reinterpret_cast<const float *>(code++));
4754 32 : continue;
4755 : }
4756 588 : case Code_ValI|Ret_Float:
4757 : {
4758 588 : args[numargs++].setfloat(static_cast<float>(static_cast<int>(op)>>8));
4759 588 : continue;
4760 : }
4761 0 : case Code_Dup|Ret_Null:
4762 : {
4763 0 : args[numargs-1].getval(args[numargs]);
4764 0 : numargs++;
4765 0 : continue;
4766 : }
4767 0 : case Code_Dup|Ret_Integer:
4768 : {
4769 0 : args[numargs].setint(args[numargs-1].getint());
4770 0 : numargs++;
4771 0 : continue;
4772 : }
4773 4 : case Code_Dup|Ret_Float:
4774 : {
4775 4 : args[numargs].setfloat(args[numargs-1].getfloat());
4776 4 : numargs++;
4777 4 : continue;
4778 : }
4779 0 : case Code_Dup|Ret_String:
4780 : {
4781 0 : args[numargs].setstr(newstring(args[numargs-1].getstr()));
4782 0 : numargs++;
4783 0 : continue;
4784 : }
4785 0 : case Code_Force|Ret_String:
4786 : {
4787 0 : forcestr(args[numargs-1]);
4788 0 : continue;
4789 : }
4790 0 : case Code_Force|Ret_Integer:
4791 : {
4792 0 : forceint(args[numargs-1]);
4793 0 : continue;
4794 : }
4795 0 : case Code_Force|Ret_Float:
4796 : {
4797 0 : forcefloat(args[numargs-1]);
4798 0 : continue;
4799 : }
4800 115 : case Code_Result|Ret_Null:
4801 : {
4802 115 : freearg(result);
4803 115 : result = args[--numargs];
4804 115 : continue;
4805 : }
4806 0 : case Code_Result|Ret_String:
4807 : case Code_Result|Ret_Integer:
4808 : case Code_Result|Ret_Float:
4809 : {
4810 0 : freearg(result);
4811 0 : result = args[--numargs];
4812 0 : forcearg(result, op&Code_RetMask);
4813 0 : continue;
4814 : }
4815 172 : case Code_Empty|Ret_Null:
4816 : {
4817 172 : args[numargs++].setcode(emptyblock[Value_Null]+1);
4818 172 : break;
4819 : }
4820 0 : case Code_Empty|Ret_String:
4821 : {
4822 0 : args[numargs++].setcode(emptyblock[Value_String]+1);
4823 0 : break;
4824 : }
4825 0 : case Code_Empty|Ret_Integer:
4826 : {
4827 0 : args[numargs++].setcode(emptyblock[Value_Integer]+1);
4828 0 : break;
4829 : }
4830 0 : case Code_Empty|Ret_Float:
4831 : {
4832 0 : args[numargs++].setcode(emptyblock[Value_Float]+1);
4833 0 : break;
4834 : }
4835 117 : case Code_Block:
4836 : {
4837 117 : uint len = op>>8;
4838 117 : args[numargs++].setcode(code+1);
4839 117 : code += len;
4840 117 : continue;
4841 117 : }
4842 0 : case Code_Compile:
4843 : {
4844 0 : tagval &arg = args[numargs-1];
4845 0 : std::vector<uint> buf;
4846 0 : switch(arg.type)
4847 : {
4848 0 : case Value_Integer:
4849 : {
4850 0 : buf.reserve(8);
4851 0 : buf.push_back(Code_Start);
4852 0 : compileint(buf, arg.i);
4853 0 : buf.push_back(Code_Result);
4854 0 : buf.push_back(Code_Exit);
4855 0 : break;
4856 : }
4857 0 : case Value_Float:
4858 : {
4859 0 : buf.reserve(8);
4860 0 : buf.push_back(Code_Start);
4861 0 : compilefloat(buf, arg.f);
4862 0 : buf.push_back(Code_Result);
4863 0 : buf.push_back(Code_Exit);
4864 0 : break;
4865 : }
4866 0 : case Value_String:
4867 : case Value_Macro:
4868 : case Value_CString:
4869 : {
4870 0 : buf.reserve(64);
4871 0 : compilemain(buf, arg.s);
4872 0 : freearg(arg);
4873 0 : break;
4874 : }
4875 0 : default:
4876 : {
4877 0 : buf.reserve(8);
4878 0 : buf.push_back(Code_Start);
4879 0 : compilenull(buf);
4880 0 : buf.push_back(Code_Result);
4881 0 : buf.push_back(Code_Exit);
4882 0 : break;
4883 : }
4884 : }
4885 0 : uint * arr = new uint[buf.size()];
4886 0 : std::memcpy(arr, buf.data(), buf.size()*sizeof(uint));
4887 0 : arg.setcode(arr+1);
4888 0 : continue;
4889 0 : }
4890 0 : case Code_Cond:
4891 : {
4892 0 : tagval &arg = args[numargs-1];
4893 0 : switch(arg.type)
4894 : {
4895 0 : case Value_String:
4896 : case Value_Macro:
4897 : case Value_CString:
4898 0 : if(arg.s[0])
4899 : {
4900 0 : std::vector<uint> buf;
4901 0 : buf.reserve(64);
4902 0 : compilemain(buf, arg.s);
4903 0 : freearg(arg);
4904 0 : uint * arr = new uint[buf.size()];
4905 0 : std::memcpy(arr, buf.data(), buf.size()*sizeof(uint));
4906 0 : arg.setcode(arr + 1);
4907 0 : }
4908 : else
4909 : {
4910 0 : forcenull(arg);
4911 : }
4912 0 : break;
4913 : }
4914 0 : continue;
4915 0 : }
4916 147 : case Code_Ident:
4917 : {
4918 147 : args[numargs++].setident(identmap[op>>8]);
4919 147 : continue;
4920 : }
4921 0 : case Code_IdentArg:
4922 : {
4923 0 : ident *id = identmap[op>>8];
4924 0 : if(!(aliasstack->usedargs&(1<<id->index)))
4925 : {
4926 0 : pusharg(*id, NullVal(), aliasstack->argstack[id->index]);
4927 0 : aliasstack->usedargs |= 1<<id->index;
4928 : }
4929 0 : args[numargs++].setident(id);
4930 0 : continue;
4931 0 : }
4932 0 : case Code_IdentU:
4933 : {
4934 0 : tagval &arg = args[numargs-1];
4935 0 : ident *id = arg.type == Value_String
4936 0 : || arg.type == Value_Macro
4937 0 : || arg.type == Value_CString ? newident(arg.s, Idf_Unknown) : dummyident;
4938 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
4939 : {
4940 0 : pusharg(*id, NullVal(), aliasstack->argstack[id->index]);
4941 0 : aliasstack->usedargs |= 1<<id->index;
4942 : }
4943 0 : freearg(arg);
4944 0 : arg.setident(id);
4945 0 : continue;
4946 0 : }
4947 :
4948 0 : case Code_LookupU|Ret_String:
4949 : #define LOOKUPU(aval, sval, ival, fval, nval) { \
4950 : tagval &arg = args[numargs-1]; \
4951 : if(arg.type != Value_String && arg.type != Value_Macro && arg.type != Value_CString) \
4952 : { \
4953 : continue; \
4954 : } \
4955 : const auto itr = idents.find(arg.s); \
4956 : if(itr != idents.end()) \
4957 : { \
4958 : ident* id = &(*(itr)).second; \
4959 : switch(id->type) \
4960 : { \
4961 : case Id_Alias: \
4962 : { \
4963 : if(id->flags&Idf_Unknown) \
4964 : { \
4965 : break; \
4966 : } \
4967 : freearg(arg); \
4968 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index))) \
4969 : { \
4970 : nval; \
4971 : continue; \
4972 : } \
4973 : aval; \
4974 : continue; \
4975 : } \
4976 : case Id_StringVar: \
4977 : { \
4978 : freearg(arg); \
4979 : sval; \
4980 : continue; \
4981 : } \
4982 : case Id_Var: \
4983 : { \
4984 : freearg(arg); \
4985 : ival; \
4986 : continue; \
4987 : } \
4988 : case Id_FloatVar: \
4989 : { \
4990 : freearg(arg); \
4991 : fval; \
4992 : continue; \
4993 : } \
4994 : case Id_Command: \
4995 : { \
4996 : freearg(arg); \
4997 : arg.setnull(); \
4998 : commandret = &arg; \
4999 : tagval buf[Max_Args]; \
5000 : callcommand(id, buf, 0, true); \
5001 : forcearg(arg, op&Code_RetMask); \
5002 : commandret = &result; \
5003 : continue; \
5004 : } \
5005 : default: \
5006 : { \
5007 : freearg(arg); \
5008 : nval; \
5009 : continue; \
5010 : } \
5011 : } \
5012 : } \
5013 : debugcode("unknown alias lookup(u): %s", arg.s); \
5014 : freearg(arg); \
5015 : nval; \
5016 : continue; \
5017 : }
5018 0 : LOOKUPU(arg.setstr(newstring(id->getstr())),
5019 : arg.setstr(newstring(*id->storage.s)),
5020 : arg.setstr(newstring(intstr(*id->storage.i))),
5021 : arg.setstr(newstring(floatstr(*id->storage.f))),
5022 : arg.setstr(newstring("")));
5023 80 : case Code_Lookup|Ret_String:
5024 : #define LOOKUP(aval) { \
5025 : ident *id = identmap[op>>8]; \
5026 : if(id->flags&Idf_Unknown) \
5027 : { \
5028 : debugcode("unknown alias lookup: %s", id->name); \
5029 : } \
5030 : aval; \
5031 : continue; \
5032 : }
5033 80 : LOOKUP(args[numargs++].setstr(newstring(id->getstr())));
5034 0 : case Code_LookupArg|Ret_String:
5035 : #define LOOKUPARG(aval, nval) { \
5036 : ident *id = identmap[op>>8]; \
5037 : if(!(aliasstack->usedargs&(1<<id->index))) \
5038 : { \
5039 : nval; \
5040 : continue; \
5041 : } \
5042 : aval; \
5043 : continue; \
5044 : }
5045 0 : LOOKUPARG(args[numargs++].setstr(newstring(id->getstr())), args[numargs++].setstr(newstring("")));
5046 0 : case Code_LookupU|Ret_Integer:
5047 : {
5048 0 : LOOKUPU(arg.setint(id->getint()),
5049 : arg.setint(static_cast<int>(strtoul(*id->storage.s, nullptr, 0))),
5050 : arg.setint(*id->storage.i),
5051 : arg.setint(static_cast<int>(*id->storage.f)),
5052 : arg.setint(0));
5053 : }
5054 202 : case Code_Lookup|Ret_Integer:
5055 202 : LOOKUP(args[numargs++].setint(id->getint()));
5056 0 : case Code_LookupArg|Ret_Integer:
5057 0 : LOOKUPARG(args[numargs++].setint(id->getint()), args[numargs++].setint(0));
5058 0 : case Code_LookupU|Ret_Float:
5059 0 : LOOKUPU(arg.setfloat(id->getfloat()),
5060 : arg.setfloat(parsefloat(*id->storage.s)),
5061 : arg.setfloat(static_cast<float>(*id->storage.i)),
5062 : arg.setfloat(*id->storage.f),
5063 : arg.setfloat(0.0f));
5064 22 : case Code_Lookup|Ret_Float:
5065 22 : LOOKUP(args[numargs++].setfloat(id->getfloat()));
5066 0 : case Code_LookupArg|Ret_Float:
5067 0 : LOOKUPARG(args[numargs++].setfloat(id->getfloat()), args[numargs++].setfloat(0.0f));
5068 0 : case Code_LookupU|Ret_Null:
5069 0 : LOOKUPU(id->getval(arg),
5070 : arg.setstr(newstring(*id->storage.s)),
5071 : arg.setint(*id->storage.i),
5072 : arg.setfloat(*id->storage.f),
5073 : arg.setnull());
5074 0 : case Code_Lookup|Ret_Null:
5075 0 : LOOKUP(id->getval(args[numargs++]));
5076 0 : case Code_LookupArg|Ret_Null:
5077 0 : LOOKUPARG(id->getval(args[numargs++]), args[numargs++].setnull());
5078 0 : case Code_LookupMU|Ret_String:
5079 0 : LOOKUPU(id->getcstr(arg),
5080 : arg.setcstr(*id->storage.s),
5081 : arg.setstr(newstring(intstr(*id->storage.i))),
5082 : arg.setstr(newstring(floatstr(*id->storage.f))),
5083 : arg.setcstr(""));
5084 84 : case Code_LookupM|Ret_String:
5085 84 : LOOKUP(id->getcstr(args[numargs++]));
5086 0 : case Code_LookupMArg|Ret_String:
5087 0 : LOOKUPARG(id->getcstr(args[numargs++]), args[numargs++].setcstr(""));
5088 0 : case Code_LookupMU|Ret_Null:
5089 0 : LOOKUPU(id->getcval(arg),
5090 : arg.setcstr(*id->storage.s),
5091 : arg.setint(*id->storage.i),
5092 : arg.setfloat(*id->storage.f),
5093 : arg.setnull());
5094 0 : case Code_LookupM|Ret_Null:
5095 0 : LOOKUP(id->getcval(args[numargs++]));
5096 0 : case Code_LookupMArg|Ret_Null:
5097 0 : LOOKUPARG(id->getcval(args[numargs++]), args[numargs++].setnull());
5098 :
5099 0 : case Code_StrVar|Ret_String:
5100 : case Code_StrVar|Ret_Null:
5101 : {
5102 0 : args[numargs++].setstr(newstring(*identmap[op>>8]->storage.s));
5103 0 : continue;
5104 : }
5105 0 : case Code_StrVar|Ret_Integer:
5106 : {
5107 0 : args[numargs++].setint(static_cast<int>(strtoul((*identmap[op>>8]->storage.s), nullptr, 0)));
5108 0 : continue;
5109 : }
5110 0 : case Code_StrVar|Ret_Float:
5111 : {
5112 0 : args[numargs++].setfloat(parsefloat(*identmap[op>>8]->storage.s));
5113 0 : continue;
5114 : }
5115 0 : case Code_StrVarM:
5116 : {
5117 0 : args[numargs++].setcstr(*identmap[op>>8]->storage.s);
5118 0 : continue;
5119 : }
5120 0 : case Code_StrVar1:
5121 : {
5122 0 : setsvarchecked(identmap[op>>8], args[--numargs].s); freearg(args[numargs]);
5123 0 : continue;
5124 : }
5125 0 : case Code_IntVar|Ret_Integer:
5126 : case Code_IntVar|Ret_Null:
5127 : {
5128 0 : args[numargs++].setint(*identmap[op>>8]->storage.i);
5129 0 : continue;
5130 : }
5131 0 : case Code_IntVar|Ret_String:
5132 : {
5133 0 : args[numargs++].setstr(newstring(intstr(*identmap[op>>8]->storage.i)));
5134 0 : continue;
5135 : }
5136 0 : case Code_IntVar|Ret_Float:
5137 : {
5138 0 : args[numargs++].setfloat(static_cast<float>(*identmap[op>>8]->storage.i));
5139 0 : continue;
5140 : }
5141 0 : case Code_IntVar1:
5142 : {
5143 0 : setvarchecked(identmap[op>>8], args[--numargs].i);
5144 0 : continue;
5145 : }
5146 0 : case Code_IntVar2:
5147 : {
5148 0 : numargs -= 2;
5149 0 : setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8));
5150 0 : continue;
5151 : }
5152 0 : case Code_IntVar3:
5153 : {
5154 0 : numargs -= 3;
5155 0 : setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8)|args[numargs+2].i);
5156 0 : continue;
5157 : }
5158 0 : case Code_FloatVar|Ret_Float:
5159 : case Code_FloatVar|Ret_Null:
5160 : {
5161 0 : args[numargs++].setfloat(*identmap[op>>8]->storage.f);
5162 0 : continue;
5163 : }
5164 0 : case Code_FloatVar|Ret_String:
5165 : {
5166 0 : args[numargs++].setstr(newstring(floatstr(*identmap[op>>8]->storage.f)));
5167 0 : continue;
5168 : }
5169 0 : case Code_FloatVar|Ret_Integer:
5170 : {
5171 0 : args[numargs++].setint(static_cast<int>(*identmap[op>>8]->storage.f));
5172 0 : continue;
5173 : }
5174 0 : case Code_FloatVar1:
5175 : {
5176 0 : setfvarchecked(identmap[op>>8], args[--numargs].f);
5177 0 : continue;
5178 : }
5179 839 : case Code_Com|Ret_Null:
5180 : case Code_Com|Ret_String:
5181 : case Code_Com|Ret_Float:
5182 : case Code_Com|Ret_Integer:
5183 : {
5184 839 : ident *id = identmap[op>>8];
5185 839 : int offset = numargs-id->numargs;
5186 839 : forcenull(result);
5187 839 : callcom(id, args, id->numargs, offset);
5188 839 : forcearg(result, op&Code_RetMask);
5189 839 : freeargs(args, numargs, offset);
5190 839 : continue;
5191 839 : }
5192 1 : case Code_ComD|Ret_Null:
5193 : case Code_ComD|Ret_String:
5194 : case Code_ComD|Ret_Float:
5195 : case Code_ComD|Ret_Integer:
5196 : {
5197 1 : ident *id = identmap[op>>8];
5198 1 : int offset = numargs-(id->numargs-1);
5199 1 : addreleaseaction(id, &args[offset], id->numargs-1);
5200 1 : callcom(id, args, id->numargs, offset);
5201 1 : forcearg(result, op&Code_RetMask);
5202 1 : freeargs(args, numargs, offset);
5203 1 : continue;
5204 1 : }
5205 :
5206 493 : case Code_ComV|Ret_Null:
5207 : case Code_ComV|Ret_String:
5208 : case Code_ComV|Ret_Float:
5209 : case Code_ComV|Ret_Integer:
5210 : {
5211 493 : ident *id = identmap[op>>13];
5212 493 : int callargs = (op>>8)&0x1F,
5213 493 : offset = numargs-callargs;
5214 493 : forcenull(result);
5215 493 : reinterpret_cast<comfunv>(id->fun)(&args[offset], callargs);
5216 493 : forcearg(result, op&Code_RetMask);
5217 493 : freeargs(args, numargs, offset);
5218 493 : continue;
5219 493 : }
5220 4 : case Code_ComC|Ret_Null:
5221 : case Code_ComC|Ret_String:
5222 : case Code_ComC|Ret_Float:
5223 : case Code_ComC|Ret_Integer:
5224 : {
5225 4 : ident *id = identmap[op>>13];
5226 4 : int callargs = (op>>8)&0x1F,
5227 4 : offset = numargs-callargs;
5228 4 : forcenull(result);
5229 : {
5230 4 : std::vector<char> buf;
5231 4 : buf.reserve(maxstrlen);
5232 4 : reinterpret_cast<comfun1>(id->fun)(conc(buf, &args[offset], callargs, true));
5233 4 : }
5234 4 : forcearg(result, op&Code_RetMask);
5235 4 : freeargs(args, numargs, offset);
5236 4 : continue;
5237 4 : }
5238 3 : case Code_ConC|Ret_Null:
5239 : case Code_ConC|Ret_String:
5240 : case Code_ConC|Ret_Float:
5241 : case Code_ConC|Ret_Integer:
5242 : case Code_ConCW|Ret_Null:
5243 : case Code_ConCW|Ret_String:
5244 : case Code_ConCW|Ret_Float:
5245 : case Code_ConCW|Ret_Integer:
5246 : {
5247 3 : int numconc = op>>8;
5248 3 : char *s = conc(&args[numargs-numconc], numconc, (op&Code_OpMask)==Code_ConC);
5249 3 : freeargs(args, numargs, numargs-numconc);
5250 3 : args[numargs].setstr(s);
5251 3 : forcearg(args[numargs], op&Code_RetMask);
5252 3 : numargs++;
5253 3 : continue;
5254 3 : }
5255 :
5256 0 : case Code_ConCM|Ret_Null:
5257 : case Code_ConCM|Ret_String:
5258 : case Code_ConCM|Ret_Float:
5259 : case Code_ConCM|Ret_Integer:
5260 : {
5261 0 : int numconc = op>>8;
5262 0 : char *s = conc(&args[numargs-numconc], numconc, false);
5263 0 : freeargs(args, numargs, numargs-numconc);
5264 0 : result.setstr(s);
5265 0 : forcearg(result, op&Code_RetMask);
5266 0 : continue;
5267 0 : }
5268 190 : case Code_Alias:
5269 : {
5270 190 : setalias(*identmap[op>>8], args[--numargs]);
5271 190 : continue;
5272 : }
5273 0 : case Code_AliasArg:
5274 : {
5275 0 : setarg(*identmap[op>>8], args[--numargs]);
5276 0 : continue;
5277 : }
5278 0 : case Code_AliasU:
5279 : {
5280 0 : numargs -= 2;
5281 0 : setalias(args[numargs].getstr(), args[numargs+1]);
5282 0 : freearg(args[numargs]);
5283 0 : continue;
5284 : }
5285 : #define SKIPARGS(offset) offset
5286 2 : case Code_Call|Ret_Null:
5287 : case Code_Call|Ret_String:
5288 : case Code_Call|Ret_Float:
5289 : case Code_Call|Ret_Integer:
5290 : {
5291 : #define FORCERESULT { \
5292 : freeargs(args, numargs, SKIPARGS(offset)); \
5293 : forcearg(result, op&Code_RetMask); \
5294 : continue; \
5295 : }
5296 : //==================================================== CALLALIAS
5297 : #define CALLALIAS { \
5298 : identstack argstack[Max_Args]; \
5299 : for(int i = 0; i < callargs; i++) \
5300 : { \
5301 : pusharg(*identmap[i], args[offset + i], argstack[i]); \
5302 : } \
5303 : int oldargs = _numargs; \
5304 : _numargs = callargs; \
5305 : int oldflags = identflags; \
5306 : identflags |= id->flags&Idf_Overridden; \
5307 : IdentLink aliaslink = { id, aliasstack, (1<<callargs)-1, argstack }; \
5308 : aliasstack = &aliaslink; \
5309 : if(!id->code) \
5310 : { \
5311 : id->code = compilecode(id->getstr()); \
5312 : } \
5313 : uint *code = id->code; \
5314 : code[0] += 0x100; \
5315 : runcode(code+1, result); \
5316 : code[0] -= 0x100; \
5317 : if(static_cast<int>(code[0]) < 0x100) \
5318 : { \
5319 : delete[] code; \
5320 : } \
5321 : aliasstack = aliaslink.next; \
5322 : identflags = oldflags; \
5323 : for(int i = 0; i < callargs; i++) \
5324 : { \
5325 : poparg(*identmap[i]); \
5326 : } \
5327 : for(int argmask = aliaslink.usedargs&(~0U<<callargs), i = callargs; argmask; i++) \
5328 : { \
5329 : if(argmask&(1<<i)) \
5330 : { \
5331 : poparg(*identmap[i]); \
5332 : argmask &= ~(1<<i); \
5333 : } \
5334 : } \
5335 : forcearg(result, op&Code_RetMask); \
5336 : _numargs = oldargs; \
5337 : numargs = SKIPARGS(offset); \
5338 : }
5339 :
5340 2 : forcenull(result);
5341 2 : ident *id = identmap[op>>13];
5342 2 : int callargs = (op>>8)&0x1F,
5343 2 : offset = numargs-callargs;
5344 2 : if(id->flags&Idf_Unknown)
5345 : {
5346 0 : debugcode("unknown command: %s", id->name);
5347 0 : FORCERESULT;
5348 : }
5349 2 : CALLALIAS;
5350 2 : continue;
5351 2 : }
5352 25 : case Code_CallArg|Ret_Null:
5353 : case Code_CallArg|Ret_String:
5354 : case Code_CallArg|Ret_Float:
5355 : case Code_CallArg|Ret_Integer:
5356 : {
5357 25 : forcenull(result);
5358 25 : ident *id = identmap[op>>13];
5359 25 : int callargs = (op>>8)&0x1F,
5360 25 : offset = numargs-callargs;
5361 25 : if(!(aliasstack->usedargs&(1<<id->index)))
5362 : {
5363 0 : FORCERESULT;
5364 : }
5365 25 : CALLALIAS;
5366 25 : continue;
5367 25 : }
5368 : #undef SKIPARGS
5369 : //==============================================================================
5370 : #define SKIPARGS(offset) offset-1
5371 0 : case Code_CallU|Ret_Null:
5372 : case Code_CallU|Ret_String:
5373 : case Code_CallU|Ret_Float:
5374 : case Code_CallU|Ret_Integer:
5375 : {
5376 0 : int callargs = op>>8,
5377 0 : offset = numargs-callargs;
5378 0 : tagval &idarg = args[offset-1];
5379 0 : if(idarg.type != Value_String && idarg.type != Value_Macro && idarg.type != Value_CString)
5380 : {
5381 0 : litval:
5382 0 : freearg(result);
5383 0 : result = idarg;
5384 0 : forcearg(result, op&Code_RetMask);
5385 0 : while(--numargs >= offset)
5386 : {
5387 0 : freearg(args[numargs]);
5388 : }
5389 0 : continue;
5390 : }
5391 0 : ident *id = nullptr;
5392 0 : const auto itr = idents.find(idarg.s);
5393 0 : if(itr != idents.end())
5394 : {
5395 0 : id = &(*(itr)).second;
5396 : }
5397 0 : if(!id)
5398 : {
5399 0 : noid:
5400 0 : if(checknumber(idarg.s))
5401 : {
5402 0 : goto litval;
5403 : }
5404 0 : debugcode("unknown command: %s", idarg.s);
5405 0 : forcenull(result);
5406 0 : FORCERESULT;
5407 : }
5408 0 : forcenull(result);
5409 0 : switch(id->type)
5410 : {
5411 0 : default:
5412 : {
5413 0 : if(!id->fun)
5414 : {
5415 0 : FORCERESULT;
5416 : }
5417 : }
5418 : [[fallthrough]];
5419 : case Id_Command:
5420 : {
5421 0 : freearg(idarg);
5422 0 : callcommand(id, &args[offset], callargs);
5423 0 : forcearg(result, op&Code_RetMask);
5424 0 : numargs = offset - 1;
5425 0 : continue;
5426 : }
5427 0 : case Id_Local:
5428 : {
5429 : identstack locals[Max_Args];
5430 0 : freearg(idarg);
5431 0 : for(int j = 0; j < callargs; ++j)
5432 : {
5433 0 : pushalias(*forceident(args[offset+j]), locals[j]);
5434 : }
5435 0 : code = runcode(code, result);
5436 0 : for(int j = 0; j < callargs; ++j)
5437 : {
5438 0 : popalias(*args[offset+j].id);
5439 : }
5440 0 : goto exit;
5441 : }
5442 0 : case Id_Var:
5443 : {
5444 0 : if(callargs <= 0)
5445 : {
5446 0 : printvar(id);
5447 : }
5448 : else
5449 : {
5450 0 : setvarchecked(id, &args[offset], callargs);
5451 : }
5452 0 : FORCERESULT;
5453 : }
5454 0 : case Id_FloatVar:
5455 0 : if(callargs <= 0)
5456 : {
5457 0 : printvar(id);
5458 : }
5459 : else
5460 : {
5461 0 : setfvarchecked(id, forcefloat(args[offset]));
5462 : }
5463 0 : FORCERESULT;
5464 0 : case Id_StringVar:
5465 0 : if(callargs <= 0)
5466 : {
5467 0 : printvar(id);
5468 : }
5469 : else
5470 : {
5471 0 : setsvarchecked(id, forcestr(args[offset]));
5472 : }
5473 0 : FORCERESULT;
5474 0 : case Id_Alias:
5475 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
5476 : {
5477 0 : FORCERESULT;
5478 : }
5479 0 : if(id->valtype==Value_Null)
5480 : {
5481 0 : goto noid;
5482 : }
5483 0 : freearg(idarg);
5484 0 : CALLALIAS;
5485 0 : continue;
5486 0 : }
5487 : }
5488 : #undef SKIPARGS
5489 2672 : }
5490 5455 : }
5491 2173 : exit:
5492 2173 : commandret = prevret;
5493 2173 : --rundepth;
5494 2173 : return code;
5495 : }
5496 :
5497 114 : void executeret(const uint *code, tagval &result)
5498 : {
5499 114 : runcode(code, result);
5500 114 : }
5501 :
5502 276 : void executeret(const char *p, tagval &result)
5503 : {
5504 276 : std::vector<uint> code;
5505 276 : code.reserve(64);
5506 276 : compilemain(code, p, Value_Any);
5507 276 : runcode(code.data()+1, result);
5508 276 : if(static_cast<int>(code[0]) >= 0x100)
5509 : {
5510 0 : uint * arr = new uint[code.size()];
5511 0 : std::memcpy(arr, code.data(), code.size()*sizeof(uint));
5512 : }
5513 276 : }
5514 :
5515 1 : void executeret(ident *id, tagval *args, int numargs, bool lookup, tagval &result)
5516 : {
5517 1 : result.setnull();
5518 1 : ++rundepth;
5519 1 : tagval *prevret = commandret;
5520 1 : commandret = &result;
5521 1 : if(rundepth > maxrundepth)
5522 : {
5523 0 : debugcode("exceeded recursion limit");
5524 : }
5525 1 : else if(id)
5526 : {
5527 1 : switch(id->type)
5528 : {
5529 0 : default:
5530 0 : if(!id->fun)
5531 : {
5532 0 : break;
5533 : }
5534 : [[fallthrough]];
5535 : case Id_Command:
5536 1 : if(numargs < id->numargs)
5537 : {
5538 : tagval buf[Max_Args];
5539 0 : std::memcpy(buf, args, numargs*sizeof(tagval)); //copy numargs number of args from passed args tagval
5540 0 : callcommand(id, buf, numargs, lookup);
5541 : }
5542 : else
5543 : {
5544 1 : callcommand(id, args, numargs, lookup);
5545 : }
5546 1 : numargs = 0;
5547 1 : break;
5548 0 : case Id_Var:
5549 0 : if(numargs <= 0)
5550 : {
5551 0 : printvar(id);
5552 : }
5553 : else
5554 : {
5555 0 : setvarchecked(id, args, numargs);
5556 : }
5557 0 : break;
5558 0 : case Id_FloatVar:
5559 0 : if(numargs <= 0)
5560 : {
5561 0 : printvar(id);
5562 : }
5563 : else
5564 : {
5565 0 : setfvarchecked(id, forcefloat(args[0]));
5566 : }
5567 0 : break;
5568 0 : case Id_StringVar:
5569 0 : if(numargs <= 0)
5570 : {
5571 0 : printvar(id);
5572 : }
5573 : else
5574 : {
5575 0 : setsvarchecked(id, forcestr(args[0]));
5576 : }
5577 0 : break;
5578 0 : case Id_Alias:
5579 : {
5580 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
5581 : {
5582 0 : break;
5583 : }
5584 0 : if(id->valtype==Value_Null)
5585 : {
5586 0 : break;
5587 : }
5588 : //C++ preprocessor abuse
5589 : //uses CALLALIAS form but substitutes in a bunch of special values
5590 : //and then undefines them immediately after
5591 : #define callargs numargs
5592 : #define offset 0
5593 : #define op Ret_Null
5594 : #define SKIPARGS(offset) offset
5595 0 : CALLALIAS;
5596 : #undef callargs
5597 : #undef offset
5598 : #undef op
5599 : #undef SKIPARGS
5600 0 : break;
5601 : }
5602 : }
5603 : }
5604 1 : freeargs(args, numargs, 0);
5605 1 : commandret = prevret;
5606 1 : --rundepth;
5607 1 : }
5608 : #undef CALLALIAS
5609 : //==============================================================================
5610 :
5611 0 : char *executestr(ident *id, tagval *args, int numargs, bool lookup)
5612 : {
5613 : tagval result;
5614 0 : executeret(id, args, numargs, lookup, result);
5615 0 : if(result.type == Value_Null)
5616 : {
5617 0 : return nullptr;
5618 : }
5619 0 : forcestr(result);
5620 0 : return result.s;
5621 : }
5622 :
5623 214 : int execute(const uint *code)
5624 : {
5625 : tagval result;
5626 214 : runcode(code, result);
5627 214 : int i = result.getint();
5628 214 : freearg(result);
5629 214 : return i;
5630 : }
5631 :
5632 1432 : int execute(const char *p)
5633 : {
5634 1432 : std::vector<uint> code;
5635 1432 : code.reserve(64);
5636 1432 : compilemain(code, p, Value_Integer);
5637 : tagval result;
5638 1432 : runcode(code.data()+1, result);
5639 1432 : if(static_cast<int>(code[0]) >= 0x100)
5640 : {
5641 0 : uint * arr = new uint[code.size()];
5642 0 : std::memcpy(arr, code.data(), code.size()*sizeof(uint));
5643 : }
5644 1432 : int i = result.getint();
5645 1432 : freearg(result);
5646 1432 : return i;
5647 1432 : }
5648 :
5649 1 : int execute(ident *id, tagval *args, int numargs, bool lookup)
5650 : {
5651 : tagval result;
5652 1 : executeret(id, args, numargs, lookup, result);
5653 1 : int i = result.getint();
5654 1 : freearg(result);
5655 1 : return i;
5656 : }
5657 :
5658 1 : int execident(const char *name, int noid, bool lookup)
5659 : {
5660 1 : ident *id = nullptr;
5661 1 : const auto itr = idents.find(name);
5662 1 : if(itr != idents.end())
5663 : {
5664 1 : id = &(*(itr)).second;
5665 : }
5666 1 : return id ? execute(id, nullptr, 0, lookup) : noid;
5667 : }
5668 :
5669 86 : bool executebool(const uint *code)
5670 : {
5671 : tagval result;
5672 86 : runcode(code, result);
5673 86 : bool b = getbool(result);
5674 86 : freearg(result);
5675 86 : return b;
5676 : }
5677 :
5678 0 : bool executebool(ident *id, tagval *args, int numargs, bool lookup)
5679 : {
5680 : tagval result;
5681 0 : executeret(id, args, numargs, lookup, result);
5682 0 : bool b = getbool(result);
5683 0 : freearg(result);
5684 0 : return b;
5685 : }
5686 :
5687 0 : static void doargs(uint *body)
5688 : {
5689 0 : if(aliasstack != &noalias)
5690 : {
5691 0 : UNDOARGS
5692 0 : executeret(body, *commandret);
5693 0 : REDOARGS
5694 : }
5695 : else
5696 : {
5697 0 : executeret(body, *commandret);
5698 : }
5699 0 : }
5700 :
5701 : std::unordered_map<std::string, DefVar> defvars;
5702 :
5703 1 : void initcscmds()
5704 : {
5705 1 : addcommand("local", static_cast<identfun>(nullptr), nullptr, Id_Local);
5706 :
5707 1 : addcommand("defvar", reinterpret_cast<identfun>(+[] (char *name, int *min, int *cur, int *max, char *onchange)
5708 : {
5709 : {
5710 1 : const auto itr = idents.find(name);
5711 1 : if(itr != idents.end())
5712 : {
5713 1 : debugcode("cannot redefine %s as a variable", name);
5714 1 : return;
5715 : }
5716 0 : name = newstring(name);
5717 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5718 0 : DefVar &def = (*(insert.first)).second;
5719 0 : def.name = name;
5720 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5721 0 : def.i = variable(name, *min, *cur, *max, &def.i, def.onchange ? DefVar::changed : nullptr, 0);
5722 : };
5723 1 : }), "siiis", Id_Command);
5724 1 : addcommand("defvarp", reinterpret_cast<identfun>(+[] (char *name, int *min, int *cur, int *max, char *onchange)
5725 : {
5726 : {
5727 1 : const auto itr = idents.find(name);
5728 1 : if(itr != idents.end())
5729 : {
5730 1 : debugcode("cannot redefine %s as a variable", name);
5731 1 : return;
5732 : }
5733 0 : name = newstring(name);
5734 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5735 0 : DefVar &def = (*(insert.first)).second;
5736 0 : def.name = name;
5737 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5738 0 : def.i = variable(name, *min, *cur, *max, &def.i, def.onchange ? DefVar::changed : nullptr, Idf_Persist);
5739 : };
5740 1 : }), "siiis", Id_Command);
5741 1 : addcommand("deffvar", reinterpret_cast<identfun>(+[] (char *name, float *min, float *cur, float *max, char *onchange)
5742 : {
5743 : {
5744 1 : const auto itr = idents.find(name);
5745 1 : if(itr != idents.end())
5746 : {
5747 1 : debugcode("cannot redefine %s as a variable", name);
5748 1 : return;
5749 : }
5750 0 : name = newstring(name);
5751 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5752 0 : DefVar &def = (*(insert.first)).second;
5753 0 : def.name = name;
5754 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5755 0 : def.f = fvariable(name, *min, *cur, *max, &def.f, def.onchange ? DefVar::changed : nullptr, 0);
5756 : };
5757 1 : }), "sfffs", Id_Command);
5758 1 : addcommand("deffvarp", reinterpret_cast<identfun>(+[] (char *name, float *min, float *cur, float *max, char *onchange)
5759 : {
5760 : {
5761 1 : const auto itr = idents.find(name);
5762 1 : if(itr != idents.end())
5763 : {
5764 1 : debugcode("cannot redefine %s as a variable", name);
5765 1 : return;
5766 : }
5767 0 : name = newstring(name);
5768 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5769 0 : DefVar &def = (*(insert.first)).second;
5770 0 : def.name = name;
5771 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5772 0 : def.f = fvariable(name, *min, *cur, *max, &def.f, def.onchange ? DefVar::changed : nullptr, Idf_Persist);
5773 : };
5774 1 : }), "sfffs", Id_Command);
5775 1 : addcommand("defsvar", reinterpret_cast<identfun>(+[] (char *name, char *cur, char *onchange)
5776 : {
5777 : {
5778 1 : const auto itr = idents.find(name);
5779 1 : if(itr != idents.end())
5780 : {
5781 1 : debugcode("cannot redefine %s as a variable", name);
5782 1 : return;
5783 : }
5784 0 : name = newstring(name);
5785 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5786 0 : DefVar &def = (*(insert.first)).second;
5787 0 : def.name = name; def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5788 0 : def.s = svariable(name, cur, &def.s, def.onchange ? DefVar::changed : nullptr, 0);
5789 : };
5790 1 : }), "sss", Id_Command);
5791 1 : addcommand("defsvarp", reinterpret_cast<identfun>(+[] (char *name, char *cur, char *onchange)
5792 : {
5793 : {
5794 1 : const auto itr = idents.find(std::string(name));
5795 1 : if(itr != idents.end())
5796 : {
5797 0 : debugcode("cannot redefine %s as a variable", name); return;
5798 : }
5799 1 : name = newstring(name);
5800 1 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5801 1 : DefVar &def = (*(insert.first)).second;
5802 1 : def.name = name;
5803 1 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5804 1 : def.s = svariable(name, cur, &def.s, def.onchange ? DefVar::changed : nullptr, Idf_Persist);
5805 : };
5806 1 : }), "sss", Id_Command);
5807 2 : addcommand("getvarmin", reinterpret_cast<identfun>(+[] (char *s) { intret(getvarmin(s)); }), "s", Id_Command);
5808 2 : addcommand("getfvarmin", reinterpret_cast<identfun>(+[] (char *s) { floatret(getfvarmin(s)); }), "s", Id_Command);
5809 2 : addcommand("getfvarmax", reinterpret_cast<identfun>(+[] (char *s) { floatret(getfvarmax(s)); }), "s", Id_Command);
5810 2 : addcommand("identexists", reinterpret_cast<identfun>(+[] (char *s) { intret(identexists(s) ? 1 : 0); }), "s", Id_Command);
5811 2 : addcommand("getalias", reinterpret_cast<identfun>(+[] (char *s) { result(getalias(s)); }), "s", Id_Command);
5812 :
5813 2 : addcommand("nodebug", reinterpret_cast<identfun>(+[] (uint *body){nodebug++; executeret(body, *commandret); nodebug--;}), "e", Id_Command);
5814 1 : addcommand("push", reinterpret_cast<identfun>(pushcmd), "rTe", Id_Command);
5815 2 : addcommand("alias", reinterpret_cast<identfun>(+[] (const char *name, tagval *v){ setalias(name, *v); v->type = Value_Null;}), "sT", Id_Command);
5816 1 : addcommand("resetvar", reinterpret_cast<identfun>(resetvar), "s", Id_Command);
5817 1 : addcommand("doargs", reinterpret_cast<identfun>(doargs), "e", Id_DoArgs);
5818 1 : }
|