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