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 : std::unordered_map<std::string, ident>::iterator 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(size_t 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 : std::unordered_map<std::string, ident>::iterator 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 : std::unordered_map<std::string, ident>::iterator 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 : std::unordered_map<std::string, ident>::iterator 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 final : 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 : std::unordered_map<std::string, ident>::iterator 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 : std::unordered_map<std::string, ident>::iterator 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 : std::unordered_map<std::string, ident>::iterator 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 : std::unordered_map<std::string, ident>::iterator 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(size_t 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 *startstring = str;
2604 0 : str = parsestring(str+1);
2605 0 : if(*str=='\"')
2606 : {
2607 0 : str++;
2608 : }
2609 0 : std::memcpy(&buf[len], startstring, str-startstring);
2610 0 : len += str-startstring;
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 : std::unordered_map<std::string, ident>::iterator 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],
3597 4 : op1 = inst1&~Code_RetMask,
3598 4 : len1 = start2 - (start1+1);
3599 4 : if(!more)
3600 : {
3601 0 : if(op1 == (Code_Block|(len1<<8)))
3602 : {
3603 0 : code[start1] = (len1<<8) | Code_JumpFalse;
3604 0 : code[start1+1] = Code_EnterResult;
3605 0 : code[start1+len1] = (code[start1+len1]&~Code_RetMask) | ret_code_any(rettype);
3606 0 : break;
3607 : }
3608 0 : compileblock(code);
3609 : }
3610 : else
3611 : {
3612 4 : uint inst2 = code[start2],
3613 4 : op2 = inst2&~Code_RetMask,
3614 4 : len2 = code.size() - (start2+1);
3615 4 : if(op2 == (Code_Block|(len2<<8)))
3616 : {
3617 4 : if(op1 == (Code_Block|(len1<<8)))
3618 : {
3619 4 : code[start1] = ((start2-start1)<<8) | Code_JumpFalse;
3620 4 : code[start1+1] = Code_EnterResult;
3621 4 : code[start1+len1] = (code[start1+len1]&~Code_RetMask) | ret_code_any(rettype);
3622 4 : code[start2] = (len2<<8) | Code_Jump;
3623 4 : code[start2+1] = Code_EnterResult;
3624 4 : code[start2+len2] = (code[start2+len2]&~Code_RetMask) | ret_code_any(rettype);
3625 4 : break;
3626 : }
3627 0 : else if(op1 == (Code_Empty|(len1<<8)))
3628 : {
3629 0 : code[start1] = Code_Null | (inst2&Code_RetMask);
3630 0 : code[start2] = (len2<<8) | Code_JumpTrue;
3631 0 : code[start2+1] = Code_EnterResult;
3632 0 : code[start2+len2] = (code[start2+len2]&~Code_RetMask) | ret_code_any(rettype);
3633 0 : break;
3634 : }
3635 : }
3636 : }
3637 0 : code.push_back(Code_Com|ret_code_any(rettype)|(id->index<<8));
3638 : }
3639 : }
3640 4 : break;
3641 : }
3642 53 : case Id_Result:
3643 : {
3644 53 : if(more)
3645 : {
3646 53 : more = compilearg(code, p, Value_Any, prevargs);
3647 : }
3648 53 : code.push_back((more ? Code_Result : Code_Null) | ret_code_any(rettype));
3649 53 : break;
3650 : }
3651 7 : case Id_Not:
3652 : {
3653 7 : if(more)
3654 : {
3655 7 : more = compilearg(code, p, Value_CAny, prevargs);
3656 : }
3657 7 : code.push_back((more ? Code_Not : Code_True) | ret_code_any(rettype));
3658 7 : break;
3659 : }
3660 14 : case Id_And:
3661 : case Id_Or:
3662 : {
3663 14 : if(more)
3664 : {
3665 14 : more = compilearg(code, p, Value_Cond, prevargs);
3666 : }
3667 14 : if(!more) //more can be affected by above assignment
3668 : {
3669 4 : code.push_back((id->type == Id_And ? Code_True : Code_False) | ret_code_any(rettype));
3670 : }
3671 : else
3672 : {
3673 10 : numargs++;
3674 10 : int start = code.size(),
3675 10 : end = start;
3676 27 : while(numargs < Max_Args)
3677 : {
3678 27 : more = compilearg(code, p, Value_Cond, prevargs+numargs);
3679 27 : if(!more)
3680 : {
3681 10 : break;
3682 : }
3683 17 : numargs++;
3684 17 : if((code[end]&~Code_RetMask) != (Code_Block|(static_cast<uint>(code.size()-(end+1))<<8)))
3685 : {
3686 0 : break;
3687 : }
3688 17 : end = code.size();
3689 : }
3690 10 : if(more)
3691 : {
3692 0 : while(numargs < Max_Args && (more = compilearg(code, p, Value_Cond, prevargs+numargs)))
3693 : {
3694 0 : numargs++;
3695 : }
3696 0 : code.push_back(Code_ComV|ret_code_any(rettype)|(numargs<<8)|(id->index<<13));
3697 : }
3698 : else
3699 : {
3700 10 : uint op = id->type == Id_And ? Code_JumpResultFalse : Code_JumpResultTrue;
3701 10 : code.push_back(op);
3702 10 : end = code.size();
3703 27 : while(start+1 < end)
3704 : {
3705 17 : uint len = code[start]>>8;
3706 17 : code[start] = ((end-(start+1))<<8) | op;
3707 17 : code[start+1] = Code_Enter;
3708 17 : code[start+len] = (code[start+len]&~Code_RetMask) | ret_code_any(rettype);
3709 17 : start += len+1;
3710 : }
3711 : }
3712 : }
3713 14 : break;
3714 : }
3715 387 : case Id_Var:
3716 : {
3717 387 : if(!(more = compilearg(code, p, Value_Integer, prevargs)))
3718 : {
3719 387 : code.push_back(Code_Print|(id->index<<8));
3720 : }
3721 0 : else if(!(id->flags&Idf_Hex) || !(more = compilearg(code, p, Value_Integer, prevargs+1)))
3722 : {
3723 0 : code.push_back(Code_IntVar1|(id->index<<8));
3724 : }
3725 0 : else if(!(more = compilearg(code, p, Value_Integer, prevargs+2)))
3726 : {
3727 0 : code.push_back(Code_IntVar2|(id->index<<8));
3728 : }
3729 : else
3730 : {
3731 0 : code.push_back(Code_IntVar3|(id->index<<8));
3732 : }
3733 387 : break;
3734 : }
3735 112 : case Id_FloatVar:
3736 : {
3737 112 : if(!(more = compilearg(code, p, Value_Float, prevargs)))
3738 : {
3739 112 : code.push_back(Code_Print|(id->index<<8));
3740 : }
3741 : else
3742 : {
3743 0 : code.push_back(Code_FloatVar1|(id->index<<8));
3744 : }
3745 112 : break;
3746 : }
3747 4 : case Id_StringVar:
3748 : {
3749 4 : if(!(more = compilearg(code, p, Value_CString, prevargs)))
3750 : {
3751 4 : code.push_back(Code_Print|(id->index<<8));
3752 : }
3753 : else
3754 : {
3755 : do
3756 : {
3757 0 : ++numargs;
3758 0 : } while(numargs < Max_Args && (more = compilearg(code, p, Value_CAny, prevargs+numargs)));
3759 0 : if(numargs > 1)
3760 : {
3761 0 : code.push_back(Code_ConC|Ret_String|(numargs<<8));
3762 : }
3763 0 : code.push_back(Code_StrVar1|(id->index<<8));
3764 : }
3765 4 : break;
3766 : }
3767 : }
3768 : }
3769 1851 : }
3770 1954 : endstatement:
3771 1954 : if(more)
3772 : {
3773 445 : while(compilearg(code, p, Value_Pop))
3774 : {
3775 : //(empty body)
3776 : }
3777 : }
3778 1954 : p += std::strcspn(p, ")];/\n\0");
3779 1954 : int c = *p++;
3780 1954 : switch(c)
3781 : {
3782 1785 : case '\0':
3783 : {
3784 1785 : if(c != brak)
3785 : {
3786 0 : debugcodeline(line, "missing \"%c\"", brak);
3787 : }
3788 1785 : p--;
3789 1785 : return;
3790 : }
3791 94 : case ')':
3792 : case ']':
3793 : {
3794 94 : if(c == brak)
3795 : {
3796 94 : return;
3797 : }
3798 0 : debugcodeline(line, "unexpected \"%c\"", c);
3799 0 : break;
3800 : }
3801 0 : case '/':
3802 : {
3803 0 : if(*p == '/')
3804 : {
3805 0 : p += std::strcspn(p, "\n\0");
3806 : }
3807 0 : goto endstatement;
3808 : }
3809 : }
3810 75 : }
3811 : }
3812 :
3813 1735 : static void compilemain(std::vector<uint> &code, const char *p, int rettype = Value_Any)
3814 : {
3815 1735 : code.push_back(Code_Start);
3816 1735 : compilestatements(code, p, Value_Any);
3817 1735 : code.push_back(Code_Exit|(rettype < Value_Any ? rettype<<Code_Ret : 0));
3818 1735 : }
3819 :
3820 28 : uint *compilecode(const char *p)
3821 : {
3822 28 : std::vector<uint> buf;
3823 28 : buf.reserve(64);
3824 28 : compilemain(buf, p);
3825 28 : uint *code = new uint[buf.size()];
3826 28 : std::memcpy(code, buf.data(), buf.size()*sizeof(uint));
3827 28 : code[0] += 0x100;
3828 28 : return code;
3829 28 : }
3830 :
3831 0 : static const uint *forcecode(tagval &v)
3832 : {
3833 0 : if(v.type != Value_Code)
3834 : {
3835 0 : std::vector<uint> buf;
3836 0 : buf.reserve(64);
3837 0 : compilemain(buf, v.getstr());
3838 0 : freearg(v);
3839 0 : uint * arr = new uint[buf.size()];
3840 0 : std::memcpy(arr, buf.data(), buf.size()*sizeof(uint));
3841 0 : v.setcode(arr+1);
3842 0 : }
3843 0 : return v.code;
3844 : }
3845 :
3846 0 : static void forcecond(tagval &v)
3847 : {
3848 0 : switch(v.type)
3849 : {
3850 0 : case Value_String:
3851 : case Value_Macro:
3852 : case Value_CString:
3853 : {
3854 0 : if(v.s[0])
3855 : {
3856 0 : forcecode(v);
3857 : }
3858 : else
3859 : {
3860 0 : v.setint(0);
3861 : }
3862 0 : break;
3863 : }
3864 : }
3865 0 : }
3866 :
3867 0 : void freecode(uint *code)
3868 : {
3869 0 : if(!code)
3870 : {
3871 0 : return;
3872 : }
3873 0 : switch(*code&Code_OpMask)
3874 : {
3875 0 : case Code_Start:
3876 : {
3877 0 : *code -= 0x100;
3878 0 : if(static_cast<int>(*code) < 0x100)
3879 : {
3880 0 : delete[] code;
3881 : }
3882 0 : return;
3883 : }
3884 : }
3885 0 : switch(code[-1]&Code_OpMask)
3886 : {
3887 0 : case Code_Start:
3888 : {
3889 0 : code[-1] -= 0x100;
3890 0 : if(static_cast<int>(code[-1]) < 0x100)
3891 : {
3892 0 : delete[] &code[-1];
3893 : }
3894 0 : break;
3895 : }
3896 0 : case Code_Offset:
3897 : {
3898 0 : code -= static_cast<int>(code[-1]>>8);
3899 0 : *code -= 0x100;
3900 0 : if(static_cast<int>(*code) < 0x100)
3901 : {
3902 0 : delete[] code;
3903 : }
3904 0 : break;
3905 : }
3906 : }
3907 : }
3908 :
3909 388 : void printvar(const ident *id, int i)
3910 : {
3911 388 : if(i < 0)
3912 : {
3913 4 : conoutf("%s = %d", id->name, i);
3914 : }
3915 384 : else if(id->flags&Idf_Hex && id->val.i.max==0xFFFFFF)
3916 : {
3917 45 : conoutf("%s = 0x%.6X (%d, %d, %d)", id->name, i, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
3918 : }
3919 : else
3920 : {
3921 339 : conoutf(id->flags&Idf_Hex ? "%s = 0x%X" : "%s = %d", id->name, i);
3922 : }
3923 388 : }
3924 :
3925 112 : void printfvar(const ident *id, float f)
3926 : {
3927 112 : conoutf("%s = %s", id->name, floatstr(f));
3928 112 : }
3929 :
3930 4 : void printsvar(const ident *id, const char *s)
3931 : {
3932 4 : conoutf(std::strchr(s, '"') ? "%s = [%s]" : "%s = \"%s\"", id->name, s);
3933 4 : }
3934 :
3935 503 : void printvar(const ident *id)
3936 : {
3937 503 : switch(id->type)
3938 : {
3939 387 : case Id_Var:
3940 : {
3941 387 : printvar(id, *id->val.storage.i);
3942 387 : break;
3943 : }
3944 112 : case Id_FloatVar:
3945 : {
3946 112 : printfvar(id, *id->val.storage.f);
3947 112 : break;
3948 : }
3949 4 : case Id_StringVar:
3950 : {
3951 4 : printsvar(id, *id->val.storage.s);
3952 4 : break;
3953 : }
3954 : }
3955 503 : }
3956 : //You know what they awoke in the darkness of Khazad-dum... shadow and flame.
3957 : // these are typedefs for argument lists of variable sizes
3958 :
3959 : // they will be used below to typecast various id->fun objects to different lengths
3960 : // which allows them to only accept certain lengths of arguments
3961 : // up to 12 args typedef'd here, could be extended with more typedefs (buy why?)
3962 :
3963 : //comfun stands for COMmand FUNction
3964 : typedef void (__cdecl *comfun)();
3965 : typedef void (__cdecl *comfun1)(void *);
3966 : typedef void (__cdecl *comfun2)(void *, void *);
3967 : typedef void (__cdecl *comfun3)(void *, void *, void *);
3968 : typedef void (__cdecl *comfun4)(void *, void *, void *, void *);
3969 : typedef void (__cdecl *comfun5)(void *, void *, void *, void *, void *);
3970 : typedef void (__cdecl *comfun6)(void *, void *, void *, void *, void *, void *);
3971 : typedef void (__cdecl *comfun7)(void *, void *, void *, void *, void *, void *, void *);
3972 : typedef void (__cdecl *comfun8)(void *, void *, void *, void *, void *, void *, void *, void *);
3973 : typedef void (__cdecl *comfun9)(void *, void *, void *, void *, void *, void *, void *, void *, void *);
3974 : typedef void (__cdecl *comfun10)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
3975 : typedef void (__cdecl *comfun11)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
3976 : typedef void (__cdecl *comfun12)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
3977 : typedef void (__cdecl *comfunv)(tagval *, int);
3978 :
3979 0 : static const uint *skipcode(const uint *code, tagval &result = noret)
3980 : {
3981 0 : int depth = 0;
3982 : for(;;)
3983 : {
3984 0 : uint op = *code++;
3985 0 : switch(op&0xFF)
3986 : {
3987 0 : case Code_Macro:
3988 : case Code_Val|Ret_String:
3989 : {
3990 0 : uint len = op>>8;
3991 0 : code += len/sizeof(uint) + 1;
3992 0 : continue;
3993 0 : }
3994 0 : case Code_Block:
3995 : case Code_Jump:
3996 : case Code_JumpTrue:
3997 : case Code_JumpFalse:
3998 : case Code_JumpResultTrue:
3999 : case Code_JumpResultFalse:
4000 : {
4001 0 : uint len = op>>8;
4002 0 : code += len;
4003 0 : continue;
4004 0 : }
4005 0 : case Code_Enter:
4006 : case Code_EnterResult:
4007 : {
4008 0 : ++depth;
4009 0 : continue;
4010 : }
4011 0 : case Code_Exit|Ret_Null:
4012 : case Code_Exit|Ret_String:
4013 : case Code_Exit|Ret_Integer:
4014 : case Code_Exit|Ret_Float:
4015 : {
4016 0 : if(depth <= 0)
4017 : {
4018 0 : if(&result != &noret)
4019 : {
4020 0 : forcearg(result, op&Code_RetMask);
4021 : }
4022 0 : return code;
4023 : }
4024 0 : --depth;
4025 0 : continue;
4026 : }
4027 0 : }
4028 0 : }
4029 : }
4030 :
4031 0 : static uint *copycode(const uint *src)
4032 : {
4033 0 : const uint *end = skipcode(src);
4034 0 : size_t len = end - src;
4035 0 : uint *dst = new uint[len + 1];
4036 0 : *dst++ = Code_Start;
4037 0 : std::memcpy(dst, src, len*sizeof(uint));
4038 0 : return dst;
4039 : }
4040 :
4041 0 : static void copyarg(tagval &dst, const tagval &src)
4042 : {
4043 0 : switch(src.type)
4044 : {
4045 0 : case Value_Integer:
4046 : case Value_Float:
4047 : case Value_Ident:
4048 : {
4049 0 : dst = src;
4050 0 : break;
4051 : }
4052 0 : case Value_String:
4053 : case Value_Macro:
4054 : case Value_CString:
4055 : {
4056 0 : dst.setstr(newstring(src.s));
4057 0 : break;
4058 : }
4059 0 : case Value_Code:
4060 : {
4061 0 : dst.setcode(copycode(src.code));
4062 0 : break;
4063 : }
4064 0 : default:
4065 : {
4066 0 : dst.setnull();
4067 0 : break;
4068 : }
4069 : }
4070 0 : }
4071 :
4072 1 : static void addreleaseaction(ident *id, tagval *args, int numargs)
4073 : {
4074 1 : tagval *dst = addreleaseaction(id, numargs+1);
4075 1 : if(dst)
4076 : {
4077 0 : args[numargs].setint(1);
4078 0 : for(int i = 0; i < numargs+1; ++i)
4079 : {
4080 0 : copyarg(dst[i], args[i]);
4081 : }
4082 : }
4083 : else
4084 : {
4085 1 : args[numargs].setint(0);
4086 : }
4087 1 : }
4088 :
4089 : /**
4090 : * @brief Returns the pointer to a string or integer argument.
4091 : * @param id the identifier, whether a string or integer.
4092 : * @param args the array of arguments.
4093 : * @param n the n-th argument to return.
4094 : * @param offset the offset for accessing and returning the n-th argument.
4095 : * @return void* the pointer to the string or integer.
4096 : */
4097 1884 : void* arg(const ident *id, tagval args[], int n, int offset = 0)
4098 : {
4099 1884 : if(id->cmd.argmask&(1<<n))
4100 : {
4101 882 : return reinterpret_cast<void *>(args[n + offset].s);
4102 : }
4103 1002 : return reinterpret_cast<void *>(&args[n + offset].i);
4104 : }
4105 :
4106 : /**
4107 : * @brief Takes a number `n` and type-mangles the id->fun field to
4108 : * whatever length function is desired. E.g. callcom(id, args, 6) takes the function
4109 : * pointer id->fun and changes its type to comfun6 (command function w/ 6 args).
4110 : * Each argument is then described by the arg() function for each argument slot.
4111 : *
4112 : * the id->fun member is a pointer to a free function with the signature
4113 : * void foo(ident *), which is then reinterpret_casted to a function with parameters
4114 : * that it originally had before being stored as a generic function pointer.
4115 : *
4116 : * @param id the identifier for the type of command.
4117 : * @param args the command arguments.
4118 : * @param n the n-th argument to return.
4119 : * @param offset the offset for accessing and returning the n-th argument.
4120 : */
4121 840 : void callcom(const ident *id, tagval args[], int n, int offset=0)
4122 : {
4123 : /**
4124 : * @brief Return the n-th argument. The lambda expression captures the `id`
4125 : * and `args` so only the parameter number `n` needs to be passed.
4126 : */
4127 1884 : auto a = [id, args, offset](int numargs)
4128 : {
4129 1884 : return arg(id, args, numargs, offset);
4130 840 : };
4131 :
4132 840 : switch(n)
4133 : {
4134 69 : case 0: reinterpret_cast<comfun>(id->fun)(); break;
4135 248 : case 1: reinterpret_cast<comfun1>(id->fun)(a(0)); break;
4136 220 : case 2: reinterpret_cast<comfun2>(id->fun)(a(0), a(1)); break;
4137 124 : case 3: reinterpret_cast<comfun3>(id->fun)(a(0), a(1), a(2)); break;
4138 107 : case 4: reinterpret_cast<comfun4>(id->fun)(a(0), a(1), a(2), a(3)); break;
4139 51 : case 5: reinterpret_cast<comfun5>(id->fun)(a(0), a(1), a(2), a(3), a(4)); break;
4140 10 : case 6: reinterpret_cast<comfun6>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5)); break;
4141 7 : case 7: reinterpret_cast<comfun7>(id->fun)(a(0), a(1), a(2), a(3), a(4), a(5), a(6)); break;
4142 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;
4143 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;
4144 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;
4145 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;
4146 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;
4147 : }
4148 840 : }
4149 :
4150 1 : static void callcommand(ident *id, tagval *args, int numargs, bool lookup = false)
4151 : {
4152 1 : int i = -1,
4153 1 : fakeargs = 0;
4154 1 : bool rep = false;
4155 1 : for(const char *fmt = id->cmd.args; *fmt; fmt++)
4156 : {
4157 0 : switch(*fmt)
4158 : {
4159 0 : case 'i':
4160 : {
4161 0 : if(++i >= numargs)
4162 : {
4163 0 : if(rep)
4164 : {
4165 0 : break;
4166 : }
4167 0 : args[i].setint(0);
4168 0 : fakeargs++;
4169 : }
4170 : else
4171 : {
4172 0 : forceint(args[i]);
4173 : }
4174 0 : break;
4175 : }
4176 0 : case 'b':
4177 : {
4178 0 : if(++i >= numargs)
4179 : {
4180 0 : if(rep)
4181 : {
4182 0 : break;
4183 : }
4184 0 : args[i].setint(INT_MIN);
4185 0 : fakeargs++;
4186 : }
4187 : else
4188 : {
4189 0 : forceint(args[i]);
4190 : }
4191 0 : break;
4192 : }
4193 0 : case 'f':
4194 : {
4195 0 : if(++i >= numargs)
4196 : {
4197 0 : if(rep)
4198 : {
4199 0 : break;
4200 : }
4201 0 : args[i].setfloat(0.0f);
4202 0 : fakeargs++;
4203 : }
4204 : else
4205 : {
4206 0 : forcefloat(args[i]);
4207 : }
4208 0 : break;
4209 : }
4210 : [[fallthrough]];
4211 0 : case 'F':
4212 : {
4213 0 : if(++i >= numargs)
4214 : {
4215 0 : if(rep)
4216 : {
4217 0 : break;
4218 : }
4219 0 : args[i].setfloat(args[i-1].getfloat());
4220 0 : fakeargs++;
4221 : }
4222 : else
4223 : {
4224 0 : forcefloat(args[i]);
4225 : }
4226 0 : break;
4227 : }
4228 0 : case 'S':
4229 : {
4230 0 : if(++i >= numargs)
4231 : {
4232 0 : if(rep)
4233 : {
4234 0 : break;
4235 : }
4236 0 : args[i].setstr(newstring(""));
4237 0 : fakeargs++;
4238 : }
4239 : else
4240 : {
4241 0 : forcestr(args[i]);
4242 : }
4243 0 : break;
4244 : }
4245 0 : case 's':
4246 : {
4247 0 : if(++i >= numargs)
4248 : {
4249 0 : if(rep)
4250 : {
4251 0 : break;
4252 : }
4253 0 : args[i].setcstr("");
4254 0 : fakeargs++;
4255 : }
4256 : else
4257 : {
4258 0 : forcestr(args[i]);
4259 : }
4260 0 : break;
4261 : }
4262 0 : case 'T':
4263 : case 't':
4264 : {
4265 0 : if(++i >= numargs)
4266 : {
4267 0 : if(rep)
4268 : {
4269 0 : break;
4270 : }
4271 0 : args[i].setnull();
4272 0 : fakeargs++;
4273 : }
4274 0 : break;
4275 : }
4276 0 : case 'E':
4277 : {
4278 0 : if(++i >= numargs)
4279 : {
4280 0 : if(rep)
4281 : {
4282 0 : break;
4283 : }
4284 0 : args[i].setnull();
4285 0 : fakeargs++;
4286 : }
4287 : else
4288 : {
4289 0 : forcecond(args[i]);
4290 : }
4291 0 : break;
4292 : }
4293 0 : case 'e':
4294 : {
4295 0 : if(++i >= numargs)
4296 : {
4297 0 : if(rep)
4298 : {
4299 0 : break;
4300 : }
4301 0 : args[i].setcode(emptyblock[Value_Null]+1);
4302 0 : fakeargs++;
4303 : }
4304 : else
4305 : {
4306 0 : forcecode(args[i]);
4307 : }
4308 0 : break;
4309 : }
4310 0 : case 'r':
4311 : {
4312 0 : if(++i >= numargs)
4313 : {
4314 0 : if(rep)
4315 : {
4316 0 : break;
4317 : }
4318 0 : args[i].setident(dummyident);
4319 0 : fakeargs++;
4320 0 : break;
4321 : }
4322 : else
4323 : {
4324 0 : forceident(args[i]);
4325 0 : break;
4326 : }
4327 : }
4328 0 : case '$':
4329 : {
4330 0 : if(++i < numargs)
4331 : {
4332 0 : freearg(args[i]);
4333 : }
4334 0 : args[i].setident(id);
4335 0 : break;
4336 : }
4337 0 : case 'N':
4338 : {
4339 0 : if(++i < numargs)
4340 : {
4341 0 : freearg(args[i]);
4342 : }
4343 0 : args[i].setint(lookup ? -1 : i-fakeargs);
4344 0 : break;
4345 : }
4346 0 : case 'D':
4347 : {
4348 0 : if(++i < numargs)
4349 : {
4350 0 : freearg(args[i]);
4351 : }
4352 0 : addreleaseaction(id, args, i);
4353 0 : fakeargs++;
4354 0 : break;
4355 : }
4356 0 : case 'C':
4357 : {
4358 0 : i = std::max(i+1, numargs);
4359 0 : std::vector<char> buf;
4360 0 : reinterpret_cast<comfun1>(id->fun)(conc(buf, args, i, true));
4361 0 : goto cleanup;
4362 0 : }
4363 0 : case 'V':
4364 : {
4365 0 : i = std::max(i+1, numargs);
4366 0 : reinterpret_cast<comfunv>(id->fun)(args, i);
4367 0 : goto cleanup;
4368 : }
4369 0 : case '1':
4370 : case '2':
4371 : case '3':
4372 : case '4':
4373 : {
4374 0 : if(i+1 < numargs)
4375 : {
4376 0 : fmt -= *fmt-'0'+1;
4377 0 : rep = true;
4378 : }
4379 0 : break;
4380 : }
4381 : }
4382 : }
4383 1 : ++i;
4384 1 : callcom(id, args, i);
4385 :
4386 1 : cleanup:
4387 1 : for(int k = 0; k < i; ++k)
4388 : {
4389 0 : freearg(args[k]);
4390 : }
4391 1 : for(; i < numargs; i++)
4392 : {
4393 0 : freearg(args[i]);
4394 : }
4395 1 : }
4396 :
4397 : static constexpr int maxrundepth = 255; //limit for rundepth (nesting depth) var below
4398 : static int rundepth = 0; //current rundepth
4399 :
4400 : #define UNDOARGS \
4401 : identstack argstack[Max_Args]; \
4402 : IdentLink *prevstack = aliasstack; \
4403 : IdentLink aliaslink; \
4404 : for(int undos = 0; prevstack != &noalias; prevstack = prevstack->next) \
4405 : { \
4406 : if(prevstack->usedargs & undoflag) \
4407 : { \
4408 : ++undos; \
4409 : } \
4410 : else if(undos > 0) \
4411 : { \
4412 : --undos; \
4413 : } \
4414 : else \
4415 : { \
4416 : prevstack = prevstack->next; \
4417 : for(int argmask = aliasstack->usedargs & ~undoflag, i = 0; argmask; argmask >>= 1, i++) \
4418 : { \
4419 : if(argmask&1) \
4420 : { \
4421 : undoarg(*identmap[i], argstack[i]); \
4422 : } \
4423 : } \
4424 : aliaslink.id = aliasstack->id; \
4425 : aliaslink.next = aliasstack; \
4426 : aliaslink.usedargs = undoflag | prevstack->usedargs; \
4427 : aliaslink.argstack = prevstack->argstack; \
4428 : aliasstack = &aliaslink; \
4429 : break; \
4430 : } \
4431 : } \
4432 :
4433 :
4434 : #define REDOARGS \
4435 : if(aliasstack == &aliaslink) \
4436 : { \
4437 : prevstack->usedargs |= aliaslink.usedargs & ~undoflag; \
4438 : aliasstack = aliaslink.next; \
4439 : for(int argmask = aliasstack->usedargs & ~undoflag, i = 0; argmask; argmask >>= 1, i++) \
4440 : { \
4441 : if(argmask&1) \
4442 : { \
4443 : redoarg(*identmap[i], argstack[i]); \
4444 : } \
4445 : } \
4446 : }
4447 :
4448 2184 : static const uint *runcode(const uint *code, tagval &result)
4449 : {
4450 2184 : result.setnull();
4451 2184 : if(rundepth >= maxrundepth)
4452 : {
4453 0 : debugcode("exceeded recursion limit");
4454 0 : return skipcode(code, result);
4455 : }
4456 2184 : ++rundepth;
4457 2184 : int numargs = 0;
4458 : tagval args[Max_Args+Max_Results],
4459 2184 : *prevret = commandret;
4460 2184 : commandret = &result;
4461 : for(;;)
4462 : {
4463 7636 : uint op = *code++;
4464 7636 : switch(op&0xFF)
4465 : {
4466 0 : case Code_Start:
4467 : case Code_Offset:
4468 : {
4469 0 : continue;
4470 : }
4471 : // For Code_Null cases, set results to null, empty, or 0 values.
4472 13 : case Code_Null|Ret_Null:
4473 : {
4474 13 : freearg(result);
4475 13 : result.setnull();
4476 13 : continue;
4477 : }
4478 0 : case Code_Null|Ret_String:
4479 : {
4480 0 : freearg(result);
4481 0 : result.setstr(newstring(""));
4482 0 : continue;
4483 : }
4484 0 : case Code_Null|Ret_Integer:
4485 : {
4486 0 : freearg(result);
4487 0 : result.setint(0);
4488 0 : continue;
4489 : }
4490 0 : case Code_Null|Ret_Float:
4491 : {
4492 0 : freearg(result);
4493 0 : result.setfloat(0.0f);
4494 0 : continue;
4495 : }
4496 : // For Code_False cases, set results to 0 values.
4497 0 : case Code_False|Ret_String:
4498 : {
4499 0 : freearg(result);
4500 0 : result.setstr(newstring("0"));
4501 0 : continue;
4502 : }
4503 2 : case Code_False|Ret_Null: // Null case left empty intentionally.
4504 : case Code_False|Ret_Integer:
4505 : {
4506 2 : freearg(result);
4507 2 : result.setint(0);
4508 2 : continue;
4509 : }
4510 0 : case Code_False|Ret_Float:
4511 : {
4512 0 : freearg(result);
4513 0 : result.setfloat(0.0f);
4514 0 : continue;
4515 : }
4516 : // For Code_False cases, set results to 1 values.
4517 0 : case Code_True|Ret_String:
4518 : {
4519 0 : freearg(result);
4520 0 : result.setstr(newstring("1"));
4521 0 : continue;
4522 : }
4523 3 : case Code_True|Ret_Null: // Null case left empty intentionally.
4524 : case Code_True|Ret_Integer:
4525 : {
4526 3 : freearg(result);
4527 3 : result.setint(1);
4528 3 : continue;
4529 : }
4530 0 : case Code_True|Ret_Float:
4531 : {
4532 0 : freearg(result);
4533 0 : result.setfloat(1.0f);
4534 0 : continue;
4535 : }
4536 : // For Code_Not cases, negate values (flip 0's and 1's).
4537 0 : case Code_Not|Ret_String:
4538 : {
4539 0 : freearg(result);
4540 0 : --numargs;
4541 0 : result.setstr(newstring(getbool(args[numargs]) ? "0" : "1"));
4542 0 : freearg(args[numargs]);
4543 0 : continue;
4544 : }
4545 6 : case Code_Not|Ret_Null: // Null case left empty intentionally.
4546 : case Code_Not|Ret_Integer:
4547 : {
4548 6 : freearg(result);
4549 6 : --numargs;
4550 6 : result.setint(getbool(args[numargs]) ? 0 : 1);
4551 6 : freearg(args[numargs]);
4552 6 : continue;
4553 : }
4554 0 : case Code_Not|Ret_Float:
4555 : {
4556 0 : freearg(result);
4557 0 : --numargs;
4558 0 : result.setfloat(getbool(args[numargs]) ? 0.0f : 1.0f);
4559 0 : freearg(args[numargs]);
4560 0 : continue;
4561 : }
4562 2 : case Code_Pop:
4563 : {
4564 2 : freearg(args[--numargs]);
4565 2 : continue;
4566 : }
4567 9 : case Code_Enter:
4568 : {
4569 9 : code = runcode(code, args[numargs++]);
4570 9 : continue;
4571 : }
4572 4 : case Code_EnterResult:
4573 : {
4574 4 : freearg(result);
4575 4 : code = runcode(code, result);
4576 4 : continue;
4577 : }
4578 1431 : case Code_Exit|Ret_String:
4579 : case Code_Exit|Ret_Integer:
4580 : case Code_Exit|Ret_Float:
4581 : {
4582 1431 : forcearg(result, op&Code_RetMask);
4583 : }
4584 : [[fallthrough]];
4585 2183 : case Code_Exit|Ret_Null:
4586 : {
4587 2183 : goto exit;
4588 : }
4589 0 : case Code_ResultArg|Ret_String:
4590 : case Code_ResultArg|Ret_Integer:
4591 : case Code_ResultArg|Ret_Float:
4592 : {
4593 0 : forcearg(result, op&Code_RetMask);
4594 : }
4595 : [[fallthrough]];
4596 108 : case Code_ResultArg|Ret_Null:
4597 : {
4598 108 : args[numargs++] = result;
4599 108 : result.setnull();
4600 108 : continue;
4601 : }
4602 503 : case Code_Print:
4603 : {
4604 503 : printvar(identmap[op>>8]);
4605 503 : continue;
4606 : }
4607 1 : case Code_Local:
4608 : {
4609 1 : freearg(result);
4610 1 : int numlocals = op>>8,
4611 1 : offset = numargs-numlocals;
4612 : identstack locals[Max_Args];
4613 1 : for(int i = 0; i < numlocals; ++i)
4614 : {
4615 0 : pushalias(*args[offset+i].id, locals[i]);
4616 : }
4617 1 : code = runcode(code, result);
4618 1 : for(int i = offset; i < numargs; i++)
4619 : {
4620 0 : popalias(*args[i].id);
4621 : }
4622 1 : goto exit;
4623 : }
4624 0 : case Code_DoArgs|Ret_Null:
4625 : case Code_DoArgs|Ret_String:
4626 : case Code_DoArgs|Ret_Integer:
4627 : case Code_DoArgs|Ret_Float:
4628 : {
4629 0 : UNDOARGS
4630 0 : freearg(result);
4631 0 : runcode(args[--numargs].code, result);
4632 0 : freearg(args[numargs]);
4633 0 : forcearg(result, op&Code_RetMask);
4634 0 : REDOARGS
4635 0 : continue;
4636 0 : }
4637 0 : case Code_Do|Ret_Null:
4638 : case Code_Do|Ret_String:
4639 : case Code_Do|Ret_Integer:
4640 : case Code_Do|Ret_Float:
4641 : {
4642 0 : freearg(result);
4643 0 : runcode(args[--numargs].code, result);
4644 0 : freearg(args[numargs]);
4645 0 : forcearg(result, op&Code_RetMask);
4646 0 : continue;
4647 : }
4648 2 : case Code_Jump:
4649 : {
4650 2 : uint len = op>>8;
4651 2 : code += len;
4652 2 : continue;
4653 2 : }
4654 0 : case Code_JumpTrue:
4655 : {
4656 0 : uint len = op>>8;
4657 0 : if(getbool(args[--numargs]))
4658 : {
4659 0 : code += len;
4660 : }
4661 0 : freearg(args[numargs]);
4662 0 : continue;
4663 0 : }
4664 4 : case Code_JumpFalse:
4665 : {
4666 4 : uint len = op>>8;
4667 4 : if(!getbool(args[--numargs]))
4668 : {
4669 2 : code += len;
4670 : }
4671 4 : freearg(args[numargs]);
4672 4 : continue;
4673 4 : }
4674 7 : case Code_JumpResultTrue:
4675 : {
4676 7 : uint len = op>>8;
4677 7 : freearg(result);
4678 7 : --numargs;
4679 7 : if(args[numargs].type == Value_Code)
4680 : {
4681 6 : runcode(args[numargs].code, result);
4682 6 : freearg(args[numargs]);
4683 : }
4684 : else
4685 : {
4686 1 : result = args[numargs];
4687 : }
4688 7 : if(getbool(result))
4689 : {
4690 4 : code += len;
4691 : }
4692 7 : continue;
4693 7 : }
4694 12 : case Code_JumpResultFalse:
4695 : {
4696 12 : uint len = op>>8;
4697 12 : freearg(result);
4698 12 : --numargs;
4699 12 : if(args[numargs].type == Value_Code)
4700 : {
4701 4 : runcode(args[numargs].code, result);
4702 4 : freearg(args[numargs]);
4703 : }
4704 : else
4705 : {
4706 8 : result = args[numargs];
4707 : }
4708 12 : if(!getbool(result))
4709 : {
4710 1 : code += len;
4711 : }
4712 12 : continue;
4713 12 : }
4714 625 : case Code_Macro:
4715 : {
4716 625 : uint len = op>>8;
4717 625 : args[numargs++].setmacro(code);
4718 625 : code += len/sizeof(uint) + 1;
4719 625 : continue;
4720 625 : }
4721 20 : case Code_Val|Ret_String:
4722 : {
4723 20 : uint len = op>>8;
4724 20 : const char * codearr = reinterpret_cast<const char *>(code);
4725 : //char * str = newstring(codearr, len);
4726 : //copystring(new char[len+1], codearr, len+1);
4727 20 : char * str = new char[len+1];
4728 20 : std::memcpy(str, codearr, len*sizeof(uchar));
4729 20 : str[len] = 0;
4730 :
4731 20 : args[numargs++].setstr(str);
4732 20 : code += len/sizeof(uint) + 1;
4733 20 : continue;
4734 20 : }
4735 67 : case Code_ValI|Ret_String:
4736 : {
4737 67 : char s[4] = { static_cast<char>((op>>8)&0xFF), static_cast<char>((op>>16)&0xFF), static_cast<char>((op>>24)&0xFF), '\0' };
4738 67 : args[numargs++].setstr(newstring(s));
4739 67 : continue;
4740 67 : }
4741 83 : case Code_Val|Ret_Null:
4742 : case Code_ValI|Ret_Null:
4743 : {
4744 83 : args[numargs++].setnull();
4745 83 : continue;
4746 : }
4747 17 : case Code_Val|Ret_Integer:
4748 : {
4749 17 : args[numargs++].setint(static_cast<int>(*code++));
4750 17 : continue;
4751 : }
4752 846 : case Code_ValI|Ret_Integer:
4753 : {
4754 846 : args[numargs++].setint(static_cast<int>(op)>>8);
4755 846 : continue;
4756 : }
4757 32 : case Code_Val|Ret_Float:
4758 : {
4759 32 : args[numargs++].setfloat(*reinterpret_cast<const float *>(code++));
4760 32 : continue;
4761 : }
4762 588 : case Code_ValI|Ret_Float:
4763 : {
4764 588 : args[numargs++].setfloat(static_cast<float>(static_cast<int>(op)>>8));
4765 588 : continue;
4766 : }
4767 0 : case Code_Dup|Ret_Null:
4768 : {
4769 0 : args[numargs-1].getval(args[numargs]);
4770 0 : numargs++;
4771 0 : continue;
4772 : }
4773 0 : case Code_Dup|Ret_Integer:
4774 : {
4775 0 : args[numargs].setint(args[numargs-1].getint());
4776 0 : numargs++;
4777 0 : continue;
4778 : }
4779 4 : case Code_Dup|Ret_Float:
4780 : {
4781 4 : args[numargs].setfloat(args[numargs-1].getfloat());
4782 4 : numargs++;
4783 4 : continue;
4784 : }
4785 0 : case Code_Dup|Ret_String:
4786 : {
4787 0 : args[numargs].setstr(newstring(args[numargs-1].getstr()));
4788 0 : numargs++;
4789 0 : continue;
4790 : }
4791 0 : case Code_Force|Ret_String:
4792 : {
4793 0 : forcestr(args[numargs-1]);
4794 0 : continue;
4795 : }
4796 0 : case Code_Force|Ret_Integer:
4797 : {
4798 0 : forceint(args[numargs-1]);
4799 0 : continue;
4800 : }
4801 0 : case Code_Force|Ret_Float:
4802 : {
4803 0 : forcefloat(args[numargs-1]);
4804 0 : continue;
4805 : }
4806 115 : case Code_Result|Ret_Null:
4807 : {
4808 115 : freearg(result);
4809 115 : result = args[--numargs];
4810 115 : continue;
4811 : }
4812 0 : case Code_Result|Ret_String:
4813 : case Code_Result|Ret_Integer:
4814 : case Code_Result|Ret_Float:
4815 : {
4816 0 : freearg(result);
4817 0 : result = args[--numargs];
4818 0 : forcearg(result, op&Code_RetMask);
4819 0 : continue;
4820 : }
4821 172 : case Code_Empty|Ret_Null:
4822 : {
4823 172 : args[numargs++].setcode(emptyblock[Value_Null]+1);
4824 172 : break;
4825 : }
4826 0 : case Code_Empty|Ret_String:
4827 : {
4828 0 : args[numargs++].setcode(emptyblock[Value_String]+1);
4829 0 : break;
4830 : }
4831 0 : case Code_Empty|Ret_Integer:
4832 : {
4833 0 : args[numargs++].setcode(emptyblock[Value_Integer]+1);
4834 0 : break;
4835 : }
4836 0 : case Code_Empty|Ret_Float:
4837 : {
4838 0 : args[numargs++].setcode(emptyblock[Value_Float]+1);
4839 0 : break;
4840 : }
4841 117 : case Code_Block:
4842 : {
4843 117 : uint len = op>>8;
4844 117 : args[numargs++].setcode(code+1);
4845 117 : code += len;
4846 117 : continue;
4847 117 : }
4848 0 : case Code_Compile:
4849 : {
4850 0 : tagval &arg = args[numargs-1];
4851 0 : std::vector<uint> buf;
4852 0 : switch(arg.type)
4853 : {
4854 0 : case Value_Integer:
4855 : {
4856 0 : buf.reserve(8);
4857 0 : buf.push_back(Code_Start);
4858 0 : compileint(buf, arg.i);
4859 0 : buf.push_back(Code_Result);
4860 0 : buf.push_back(Code_Exit);
4861 0 : break;
4862 : }
4863 0 : case Value_Float:
4864 : {
4865 0 : buf.reserve(8);
4866 0 : buf.push_back(Code_Start);
4867 0 : compilefloat(buf, arg.f);
4868 0 : buf.push_back(Code_Result);
4869 0 : buf.push_back(Code_Exit);
4870 0 : break;
4871 : }
4872 0 : case Value_String:
4873 : case Value_Macro:
4874 : case Value_CString:
4875 : {
4876 0 : buf.reserve(64);
4877 0 : compilemain(buf, arg.s);
4878 0 : freearg(arg);
4879 0 : break;
4880 : }
4881 0 : default:
4882 : {
4883 0 : buf.reserve(8);
4884 0 : buf.push_back(Code_Start);
4885 0 : compilenull(buf);
4886 0 : buf.push_back(Code_Result);
4887 0 : buf.push_back(Code_Exit);
4888 0 : break;
4889 : }
4890 : }
4891 0 : uint * arr = new uint[buf.size()];
4892 0 : std::memcpy(arr, buf.data(), buf.size()*sizeof(uint));
4893 0 : arg.setcode(arr+1);
4894 0 : continue;
4895 0 : }
4896 0 : case Code_Cond:
4897 : {
4898 0 : tagval &arg = args[numargs-1];
4899 0 : switch(arg.type)
4900 : {
4901 0 : case Value_String:
4902 : case Value_Macro:
4903 : case Value_CString:
4904 0 : if(arg.s[0])
4905 : {
4906 0 : std::vector<uint> buf;
4907 0 : buf.reserve(64);
4908 0 : compilemain(buf, arg.s);
4909 0 : freearg(arg);
4910 0 : uint * arr = new uint[buf.size()];
4911 0 : std::memcpy(arr, buf.data(), buf.size()*sizeof(uint));
4912 0 : arg.setcode(arr + 1);
4913 0 : }
4914 : else
4915 : {
4916 0 : forcenull(arg);
4917 : }
4918 0 : break;
4919 : }
4920 0 : continue;
4921 0 : }
4922 147 : case Code_Ident:
4923 : {
4924 147 : args[numargs++].setident(identmap[op>>8]);
4925 147 : continue;
4926 : }
4927 0 : case Code_IdentArg:
4928 : {
4929 0 : ident *id = identmap[op>>8];
4930 0 : if(!(aliasstack->usedargs&(1<<id->index)))
4931 : {
4932 0 : pusharg(*id, NullVal(), aliasstack->argstack[id->index]);
4933 0 : aliasstack->usedargs |= 1<<id->index;
4934 : }
4935 0 : args[numargs++].setident(id);
4936 0 : continue;
4937 0 : }
4938 0 : case Code_IdentU:
4939 : {
4940 0 : tagval &arg = args[numargs-1];
4941 0 : ident *id = arg.type == Value_String
4942 0 : || arg.type == Value_Macro
4943 0 : || arg.type == Value_CString ? newident(arg.s, Idf_Unknown) : dummyident;
4944 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
4945 : {
4946 0 : pusharg(*id, NullVal(), aliasstack->argstack[id->index]);
4947 0 : aliasstack->usedargs |= 1<<id->index;
4948 : }
4949 0 : freearg(arg);
4950 0 : arg.setident(id);
4951 0 : continue;
4952 0 : }
4953 :
4954 0 : case Code_LookupU|Ret_String:
4955 : #define LOOKUPU(aval, sval, ival, fval, nval) { \
4956 : tagval &arg = args[numargs-1]; \
4957 : if(arg.type != Value_String && arg.type != Value_Macro && arg.type != Value_CString) \
4958 : { \
4959 : continue; \
4960 : } \
4961 : std::unordered_map<std::string, ident>::iterator itr = idents.find(arg.s); \
4962 : if(itr != idents.end()) \
4963 : { \
4964 : ident* id = &(*(itr)).second; \
4965 : switch(id->type) \
4966 : { \
4967 : case Id_Alias: \
4968 : { \
4969 : if(id->flags&Idf_Unknown) \
4970 : { \
4971 : break; \
4972 : } \
4973 : freearg(arg); \
4974 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index))) \
4975 : { \
4976 : nval; \
4977 : continue; \
4978 : } \
4979 : aval; \
4980 : continue; \
4981 : } \
4982 : case Id_StringVar: \
4983 : { \
4984 : freearg(arg); \
4985 : sval; \
4986 : continue; \
4987 : } \
4988 : case Id_Var: \
4989 : { \
4990 : freearg(arg); \
4991 : ival; \
4992 : continue; \
4993 : } \
4994 : case Id_FloatVar: \
4995 : { \
4996 : freearg(arg); \
4997 : fval; \
4998 : continue; \
4999 : } \
5000 : case Id_Command: \
5001 : { \
5002 : freearg(arg); \
5003 : arg.setnull(); \
5004 : commandret = &arg; \
5005 : tagval buf[Max_Args]; \
5006 : callcommand(id, buf, 0, true); \
5007 : forcearg(arg, op&Code_RetMask); \
5008 : commandret = &result; \
5009 : continue; \
5010 : } \
5011 : default: \
5012 : { \
5013 : freearg(arg); \
5014 : nval; \
5015 : continue; \
5016 : } \
5017 : } \
5018 : } \
5019 : debugcode("unknown alias lookup(u): %s", arg.s); \
5020 : freearg(arg); \
5021 : nval; \
5022 : continue; \
5023 : }
5024 0 : LOOKUPU(arg.setstr(newstring(id->getstr())),
5025 : arg.setstr(newstring(*id->val.storage.s)),
5026 : arg.setstr(newstring(intstr(*id->val.storage.i))),
5027 : arg.setstr(newstring(floatstr(*id->val.storage.f))),
5028 : arg.setstr(newstring("")));
5029 80 : case Code_Lookup|Ret_String:
5030 : #define LOOKUP(aval) { \
5031 : ident * const id = identmap[op>>8]; \
5032 : if(id->flags&Idf_Unknown) \
5033 : { \
5034 : debugcode("unknown alias lookup: %s", id->name); \
5035 : } \
5036 : aval; \
5037 : continue; \
5038 : }
5039 80 : LOOKUP(args[numargs++].setstr(newstring(id->getstr())));
5040 0 : case Code_LookupArg|Ret_String:
5041 : #define LOOKUPARG(aval, nval) { \
5042 : ident * const id = identmap[op>>8]; \
5043 : if(!(aliasstack->usedargs&(1<<id->index))) \
5044 : { \
5045 : nval; \
5046 : continue; \
5047 : } \
5048 : aval; \
5049 : continue; \
5050 : }
5051 0 : LOOKUPARG(args[numargs++].setstr(newstring(id->getstr())), args[numargs++].setstr(newstring("")));
5052 0 : case Code_LookupU|Ret_Integer:
5053 : {
5054 0 : LOOKUPU(arg.setint(id->getint()),
5055 : arg.setint(static_cast<int>(std::strtoul(*id->val.storage.s, nullptr, 0))),
5056 : arg.setint(*id->val.storage.i),
5057 : arg.setint(static_cast<int>(*id->val.storage.f)),
5058 : arg.setint(0));
5059 : }
5060 202 : case Code_Lookup|Ret_Integer:
5061 202 : LOOKUP(args[numargs++].setint(id->getint()));
5062 0 : case Code_LookupArg|Ret_Integer:
5063 0 : LOOKUPARG(args[numargs++].setint(id->getint()), args[numargs++].setint(0));
5064 0 : case Code_LookupU|Ret_Float:
5065 0 : LOOKUPU(arg.setfloat(id->getfloat()),
5066 : arg.setfloat(parsefloat(*id->val.storage.s)),
5067 : arg.setfloat(static_cast<float>(*id->val.storage.i)),
5068 : arg.setfloat(*id->val.storage.f),
5069 : arg.setfloat(0.0f));
5070 22 : case Code_Lookup|Ret_Float:
5071 22 : LOOKUP(args[numargs++].setfloat(id->getfloat()));
5072 0 : case Code_LookupArg|Ret_Float:
5073 0 : LOOKUPARG(args[numargs++].setfloat(id->getfloat()), args[numargs++].setfloat(0.0f));
5074 0 : case Code_LookupU|Ret_Null:
5075 0 : LOOKUPU(id->getval(arg),
5076 : arg.setstr(newstring(*id->val.storage.s)),
5077 : arg.setint(*id->val.storage.i),
5078 : arg.setfloat(*id->val.storage.f),
5079 : arg.setnull());
5080 0 : case Code_Lookup|Ret_Null:
5081 0 : LOOKUP(id->getval(args[numargs++]));
5082 0 : case Code_LookupArg|Ret_Null:
5083 0 : LOOKUPARG(id->getval(args[numargs++]), args[numargs++].setnull());
5084 0 : case Code_LookupMU|Ret_String:
5085 0 : LOOKUPU(id->getcstr(arg),
5086 : arg.setcstr(*id->val.storage.s),
5087 : arg.setstr(newstring(intstr(*id->val.storage.i))),
5088 : arg.setstr(newstring(floatstr(*id->val.storage.f))),
5089 : arg.setcstr(""));
5090 84 : case Code_LookupM|Ret_String:
5091 84 : LOOKUP(id->getcstr(args[numargs++]));
5092 0 : case Code_LookupMArg|Ret_String:
5093 0 : LOOKUPARG(id->getcstr(args[numargs++]), args[numargs++].setcstr(""));
5094 0 : case Code_LookupMU|Ret_Null:
5095 0 : LOOKUPU(id->getcval(arg),
5096 : arg.setcstr(*id->val.storage.s),
5097 : arg.setint(*id->val.storage.i),
5098 : arg.setfloat(*id->val.storage.f),
5099 : arg.setnull());
5100 0 : case Code_LookupM|Ret_Null:
5101 0 : LOOKUP(id->getcval(args[numargs++]));
5102 0 : case Code_LookupMArg|Ret_Null:
5103 0 : LOOKUPARG(id->getcval(args[numargs++]), args[numargs++].setnull());
5104 :
5105 0 : case Code_StrVar|Ret_String:
5106 : case Code_StrVar|Ret_Null:
5107 : {
5108 0 : args[numargs++].setstr(newstring(*identmap[op>>8]->val.storage.s));
5109 0 : continue;
5110 : }
5111 0 : case Code_StrVar|Ret_Integer:
5112 : {
5113 0 : args[numargs++].setint(static_cast<int>(std::strtoul((*identmap[op>>8]->val.storage.s), nullptr, 0)));
5114 0 : continue;
5115 : }
5116 0 : case Code_StrVar|Ret_Float:
5117 : {
5118 0 : args[numargs++].setfloat(parsefloat(*identmap[op>>8]->val.storage.s));
5119 0 : continue;
5120 : }
5121 0 : case Code_StrVarM:
5122 : {
5123 0 : args[numargs++].setcstr(*identmap[op>>8]->val.storage.s);
5124 0 : continue;
5125 : }
5126 0 : case Code_StrVar1:
5127 : {
5128 0 : setsvarchecked(identmap[op>>8], args[--numargs].s); freearg(args[numargs]);
5129 0 : continue;
5130 : }
5131 0 : case Code_IntVar|Ret_Integer:
5132 : case Code_IntVar|Ret_Null:
5133 : {
5134 0 : args[numargs++].setint(*identmap[op>>8]->val.storage.i);
5135 0 : continue;
5136 : }
5137 0 : case Code_IntVar|Ret_String:
5138 : {
5139 0 : args[numargs++].setstr(newstring(intstr(*identmap[op>>8]->val.storage.i)));
5140 0 : continue;
5141 : }
5142 0 : case Code_IntVar|Ret_Float:
5143 : {
5144 0 : args[numargs++].setfloat(static_cast<float>(*identmap[op>>8]->val.storage.i));
5145 0 : continue;
5146 : }
5147 0 : case Code_IntVar1:
5148 : {
5149 0 : setvarchecked(identmap[op>>8], args[--numargs].i);
5150 0 : continue;
5151 : }
5152 0 : case Code_IntVar2:
5153 : {
5154 0 : numargs -= 2;
5155 0 : setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8));
5156 0 : continue;
5157 : }
5158 0 : case Code_IntVar3:
5159 : {
5160 0 : numargs -= 3;
5161 0 : setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8)|args[numargs+2].i);
5162 0 : continue;
5163 : }
5164 0 : case Code_FloatVar|Ret_Float:
5165 : case Code_FloatVar|Ret_Null:
5166 : {
5167 0 : args[numargs++].setfloat(*identmap[op>>8]->val.storage.f);
5168 0 : continue;
5169 : }
5170 0 : case Code_FloatVar|Ret_String:
5171 : {
5172 0 : args[numargs++].setstr(newstring(floatstr(*identmap[op>>8]->val.storage.f)));
5173 0 : continue;
5174 : }
5175 0 : case Code_FloatVar|Ret_Integer:
5176 : {
5177 0 : args[numargs++].setint(static_cast<int>(*identmap[op>>8]->val.storage.f));
5178 0 : continue;
5179 : }
5180 0 : case Code_FloatVar1:
5181 : {
5182 0 : setfvarchecked(identmap[op>>8], args[--numargs].f);
5183 0 : continue;
5184 : }
5185 838 : case Code_Com|Ret_Null:
5186 : case Code_Com|Ret_String:
5187 : case Code_Com|Ret_Float:
5188 : case Code_Com|Ret_Integer:
5189 : {
5190 838 : ident * const id = identmap[op>>8];
5191 838 : int offset = numargs-id->numargs;
5192 838 : forcenull(result);
5193 838 : callcom(id, args, id->numargs, offset);
5194 838 : forcearg(result, op&Code_RetMask);
5195 838 : freeargs(args, numargs, offset);
5196 838 : continue;
5197 838 : }
5198 1 : case Code_ComD|Ret_Null:
5199 : case Code_ComD|Ret_String:
5200 : case Code_ComD|Ret_Float:
5201 : case Code_ComD|Ret_Integer:
5202 : {
5203 1 : ident * const id = identmap[op>>8];
5204 1 : int offset = numargs-(id->numargs-1);
5205 1 : addreleaseaction(id, &args[offset], id->numargs-1);
5206 1 : callcom(id, args, id->numargs, offset);
5207 1 : forcearg(result, op&Code_RetMask);
5208 1 : freeargs(args, numargs, offset);
5209 1 : continue;
5210 1 : }
5211 :
5212 493 : case Code_ComV|Ret_Null:
5213 : case Code_ComV|Ret_String:
5214 : case Code_ComV|Ret_Float:
5215 : case Code_ComV|Ret_Integer:
5216 : {
5217 493 : ident * const id = identmap[op>>13];
5218 493 : int callargs = (op>>8)&0x1F,
5219 493 : offset = numargs-callargs;
5220 493 : forcenull(result);
5221 493 : reinterpret_cast<comfunv>(id->fun)(&args[offset], callargs);
5222 493 : forcearg(result, op&Code_RetMask);
5223 493 : freeargs(args, numargs, offset);
5224 493 : continue;
5225 493 : }
5226 4 : case Code_ComC|Ret_Null:
5227 : case Code_ComC|Ret_String:
5228 : case Code_ComC|Ret_Float:
5229 : case Code_ComC|Ret_Integer:
5230 : {
5231 4 : ident * const id = identmap[op>>13];
5232 4 : int callargs = (op>>8)&0x1F,
5233 4 : offset = numargs-callargs;
5234 4 : forcenull(result);
5235 : {
5236 4 : std::vector<char> buf;
5237 4 : buf.reserve(maxstrlen);
5238 4 : reinterpret_cast<comfun1>(id->fun)(conc(buf, &args[offset], callargs, true));
5239 4 : }
5240 4 : forcearg(result, op&Code_RetMask);
5241 4 : freeargs(args, numargs, offset);
5242 4 : continue;
5243 4 : }
5244 3 : case Code_ConC|Ret_Null:
5245 : case Code_ConC|Ret_String:
5246 : case Code_ConC|Ret_Float:
5247 : case Code_ConC|Ret_Integer:
5248 : case Code_ConCW|Ret_Null:
5249 : case Code_ConCW|Ret_String:
5250 : case Code_ConCW|Ret_Float:
5251 : case Code_ConCW|Ret_Integer:
5252 : {
5253 3 : int numconc = op>>8;
5254 3 : char *s = conc(&args[numargs-numconc], numconc, (op&Code_OpMask)==Code_ConC);
5255 3 : freeargs(args, numargs, numargs-numconc);
5256 3 : args[numargs].setstr(s);
5257 3 : forcearg(args[numargs], op&Code_RetMask);
5258 3 : numargs++;
5259 3 : continue;
5260 3 : }
5261 :
5262 0 : case Code_ConCM|Ret_Null:
5263 : case Code_ConCM|Ret_String:
5264 : case Code_ConCM|Ret_Float:
5265 : case Code_ConCM|Ret_Integer:
5266 : {
5267 0 : int numconc = op>>8;
5268 0 : char *s = conc(&args[numargs-numconc], numconc, false);
5269 0 : freeargs(args, numargs, numargs-numconc);
5270 0 : result.setstr(s);
5271 0 : forcearg(result, op&Code_RetMask);
5272 0 : continue;
5273 0 : }
5274 190 : case Code_Alias:
5275 : {
5276 190 : setalias(*identmap[op>>8], args[--numargs]);
5277 190 : continue;
5278 : }
5279 0 : case Code_AliasArg:
5280 : {
5281 0 : setarg(*identmap[op>>8], args[--numargs]);
5282 0 : continue;
5283 : }
5284 0 : case Code_AliasU:
5285 : {
5286 0 : numargs -= 2;
5287 0 : setalias(args[numargs].getstr(), args[numargs+1]);
5288 0 : freearg(args[numargs]);
5289 0 : continue;
5290 : }
5291 : #define SKIPARGS(offset) offset
5292 2 : case Code_Call|Ret_Null:
5293 : case Code_Call|Ret_String:
5294 : case Code_Call|Ret_Float:
5295 : case Code_Call|Ret_Integer:
5296 : {
5297 : #define FORCERESULT { \
5298 : freeargs(args, numargs, SKIPARGS(offset)); \
5299 : forcearg(result, op&Code_RetMask); \
5300 : continue; \
5301 : }
5302 : //==================================================== CALLALIAS
5303 : #define CALLALIAS { \
5304 : identstack argstack[Max_Args]; \
5305 : for(int i = 0; i < callargs; i++) \
5306 : { \
5307 : pusharg(*identmap[i], args[offset + i], argstack[i]); \
5308 : } \
5309 : int oldargs = _numargs; \
5310 : _numargs = callargs; \
5311 : int oldflags = identflags; \
5312 : identflags |= id->flags&Idf_Overridden; \
5313 : IdentLink aliaslink = { id, aliasstack, (1<<callargs)-1, argstack }; \
5314 : aliasstack = &aliaslink; \
5315 : if(!id->alias.code) \
5316 : { \
5317 : id->alias.code = compilecode(id->getstr()); \
5318 : } \
5319 : uint *code = id->alias.code; \
5320 : code[0] += 0x100; \
5321 : runcode(code+1, result); \
5322 : code[0] -= 0x100; \
5323 : if(static_cast<int>(code[0]) < 0x100) \
5324 : { \
5325 : delete[] code; \
5326 : } \
5327 : aliasstack = aliaslink.next; \
5328 : identflags = oldflags; \
5329 : for(int i = 0; i < callargs; i++) \
5330 : { \
5331 : poparg(*identmap[i]); \
5332 : } \
5333 : for(int argmask = aliaslink.usedargs&(~0U<<callargs), i = callargs; argmask; i++) \
5334 : { \
5335 : if(argmask&(1<<i)) \
5336 : { \
5337 : poparg(*identmap[i]); \
5338 : argmask &= ~(1<<i); \
5339 : } \
5340 : } \
5341 : forcearg(result, op&Code_RetMask); \
5342 : _numargs = oldargs; \
5343 : numargs = SKIPARGS(offset); \
5344 : }
5345 :
5346 2 : forcenull(result);
5347 2 : ident *id = identmap[op>>13];
5348 2 : int callargs = (op>>8)&0x1F,
5349 2 : offset = numargs-callargs;
5350 2 : if(id->flags&Idf_Unknown)
5351 : {
5352 0 : debugcode("unknown command: %s", id->name);
5353 0 : FORCERESULT;
5354 : }
5355 2 : CALLALIAS;
5356 2 : continue;
5357 2 : }
5358 25 : case Code_CallArg|Ret_Null:
5359 : case Code_CallArg|Ret_String:
5360 : case Code_CallArg|Ret_Float:
5361 : case Code_CallArg|Ret_Integer:
5362 : {
5363 25 : forcenull(result);
5364 25 : ident *id = identmap[op>>13];
5365 25 : int callargs = (op>>8)&0x1F,
5366 25 : offset = numargs-callargs;
5367 25 : if(!(aliasstack->usedargs&(1<<id->index)))
5368 : {
5369 0 : FORCERESULT;
5370 : }
5371 25 : CALLALIAS;
5372 25 : continue;
5373 25 : }
5374 : #undef SKIPARGS
5375 : //==============================================================================
5376 : #define SKIPARGS(offset) offset-1
5377 0 : case Code_CallU|Ret_Null:
5378 : case Code_CallU|Ret_String:
5379 : case Code_CallU|Ret_Float:
5380 : case Code_CallU|Ret_Integer:
5381 : {
5382 0 : int callargs = op>>8,
5383 0 : offset = numargs-callargs;
5384 0 : tagval &idarg = args[offset-1];
5385 0 : if(idarg.type != Value_String && idarg.type != Value_Macro && idarg.type != Value_CString)
5386 : {
5387 0 : litval:
5388 0 : freearg(result);
5389 0 : result = idarg;
5390 0 : forcearg(result, op&Code_RetMask);
5391 0 : while(--numargs >= offset)
5392 : {
5393 0 : freearg(args[numargs]);
5394 : }
5395 0 : continue;
5396 : }
5397 0 : ident *id = nullptr;
5398 0 : std::unordered_map<std::string, ident>::iterator itr = idents.find(idarg.s);
5399 0 : if(itr != idents.end())
5400 : {
5401 0 : id = &(*(itr)).second;
5402 : }
5403 0 : if(!id)
5404 : {
5405 0 : noid:
5406 0 : if(checknumber(idarg.s))
5407 : {
5408 0 : goto litval;
5409 : }
5410 0 : debugcode("unknown command: %s", idarg.s);
5411 0 : forcenull(result);
5412 0 : FORCERESULT;
5413 : }
5414 0 : forcenull(result);
5415 0 : switch(id->type)
5416 : {
5417 0 : default:
5418 : {
5419 0 : if(!id->fun)
5420 : {
5421 0 : FORCERESULT;
5422 : }
5423 : }
5424 : [[fallthrough]];
5425 : case Id_Command:
5426 : {
5427 0 : freearg(idarg);
5428 0 : callcommand(id, &args[offset], callargs);
5429 0 : forcearg(result, op&Code_RetMask);
5430 0 : numargs = offset - 1;
5431 0 : continue;
5432 : }
5433 0 : case Id_Local:
5434 : {
5435 : std::array<identstack, Max_Args> locals;
5436 0 : freearg(idarg);
5437 0 : for(int j = 0; j < callargs; ++j)
5438 : {
5439 0 : pushalias(*forceident(args[offset+j]), locals[j]);
5440 : }
5441 0 : code = runcode(code, result);
5442 0 : for(int j = 0; j < callargs; ++j)
5443 : {
5444 0 : popalias(*args[offset+j].id);
5445 : }
5446 0 : goto exit;
5447 : }
5448 0 : case Id_Var:
5449 : {
5450 0 : if(callargs <= 0)
5451 : {
5452 0 : printvar(id);
5453 : }
5454 : else
5455 : {
5456 0 : setvarchecked(id, &args[offset], callargs);
5457 : }
5458 0 : FORCERESULT;
5459 : }
5460 0 : case Id_FloatVar:
5461 0 : if(callargs <= 0)
5462 : {
5463 0 : printvar(id);
5464 : }
5465 : else
5466 : {
5467 0 : setfvarchecked(id, forcefloat(args[offset]));
5468 : }
5469 0 : FORCERESULT;
5470 0 : case Id_StringVar:
5471 0 : if(callargs <= 0)
5472 : {
5473 0 : printvar(id);
5474 : }
5475 : else
5476 : {
5477 0 : setsvarchecked(id, forcestr(args[offset]));
5478 : }
5479 0 : FORCERESULT;
5480 0 : case Id_Alias:
5481 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
5482 : {
5483 0 : FORCERESULT;
5484 : }
5485 0 : if(id->valtype==Value_Null)
5486 : {
5487 0 : goto noid;
5488 : }
5489 0 : freearg(idarg);
5490 0 : CALLALIAS;
5491 0 : continue;
5492 0 : }
5493 : }
5494 : #undef SKIPARGS
5495 2672 : }
5496 5452 : }
5497 2184 : exit:
5498 2184 : commandret = prevret;
5499 2184 : --rundepth;
5500 2184 : return code;
5501 : }
5502 :
5503 114 : void executeret(const uint *code, tagval &result)
5504 : {
5505 114 : runcode(code, result);
5506 114 : }
5507 :
5508 276 : void executeret(const char *p, tagval &result)
5509 : {
5510 276 : std::vector<uint> code;
5511 276 : code.reserve(64);
5512 276 : compilemain(code, p, Value_Any);
5513 276 : runcode(code.data()+1, result);
5514 276 : if(static_cast<int>(code[0]) >= 0x100)
5515 : {
5516 0 : uint *arr = new uint[code.size()];
5517 0 : std::memcpy(arr, code.data(), code.size()*sizeof(uint));
5518 : }
5519 276 : }
5520 :
5521 1 : void executeret(ident *id, tagval *args, int numargs, bool lookup, tagval &result)
5522 : {
5523 1 : result.setnull();
5524 1 : ++rundepth;
5525 1 : tagval *prevret = commandret;
5526 1 : commandret = &result;
5527 1 : if(rundepth > maxrundepth)
5528 : {
5529 0 : debugcode("exceeded recursion limit");
5530 : }
5531 1 : else if(id)
5532 : {
5533 1 : switch(id->type)
5534 : {
5535 0 : default:
5536 0 : if(!id->fun)
5537 : {
5538 0 : break;
5539 : }
5540 : [[fallthrough]];
5541 : case Id_Command:
5542 1 : if(numargs < id->numargs)
5543 : {
5544 : tagval buf[Max_Args];
5545 0 : std::memcpy(buf, args, numargs*sizeof(tagval)); //copy numargs number of args from passed args tagval
5546 0 : callcommand(id, buf, numargs, lookup);
5547 : }
5548 : else
5549 : {
5550 1 : callcommand(id, args, numargs, lookup);
5551 : }
5552 1 : numargs = 0;
5553 1 : break;
5554 0 : case Id_Var:
5555 0 : if(numargs <= 0)
5556 : {
5557 0 : printvar(id);
5558 : }
5559 : else
5560 : {
5561 0 : setvarchecked(id, args, numargs);
5562 : }
5563 0 : break;
5564 0 : case Id_FloatVar:
5565 0 : if(numargs <= 0)
5566 : {
5567 0 : printvar(id);
5568 : }
5569 : else
5570 : {
5571 0 : setfvarchecked(id, forcefloat(args[0]));
5572 : }
5573 0 : break;
5574 0 : case Id_StringVar:
5575 0 : if(numargs <= 0)
5576 : {
5577 0 : printvar(id);
5578 : }
5579 : else
5580 : {
5581 0 : setsvarchecked(id, forcestr(args[0]));
5582 : }
5583 0 : break;
5584 0 : case Id_Alias:
5585 : {
5586 0 : if(id->index < Max_Args && !(aliasstack->usedargs&(1<<id->index)))
5587 : {
5588 0 : break;
5589 : }
5590 0 : if(id->valtype==Value_Null)
5591 : {
5592 0 : break;
5593 : }
5594 : //C++ preprocessor abuse
5595 : //uses CALLALIAS form but substitutes in a bunch of special values
5596 : //and then undefines them immediately after
5597 : #define callargs numargs
5598 : #define offset 0
5599 : #define op Ret_Null
5600 : #define SKIPARGS(offset) offset
5601 0 : CALLALIAS;
5602 : #undef callargs
5603 : #undef offset
5604 : #undef op
5605 : #undef SKIPARGS
5606 0 : break;
5607 : }
5608 : }
5609 : }
5610 1 : freeargs(args, numargs, 0);
5611 1 : commandret = prevret;
5612 1 : --rundepth;
5613 1 : }
5614 : #undef CALLALIAS
5615 : //==============================================================================
5616 :
5617 0 : char *executestr(ident *id, tagval *args, int numargs, bool lookup)
5618 : {
5619 : tagval result;
5620 0 : executeret(id, args, numargs, lookup, result);
5621 0 : if(result.type == Value_Null)
5622 : {
5623 0 : return nullptr;
5624 : }
5625 0 : forcestr(result);
5626 0 : return result.s;
5627 : }
5628 :
5629 226 : int execute(const uint *code)
5630 : {
5631 : tagval result;
5632 226 : runcode(code, result);
5633 226 : int i = result.getint();
5634 226 : freearg(result);
5635 226 : return i;
5636 : }
5637 :
5638 1431 : int execute(const char *p)
5639 : {
5640 1431 : std::vector<uint> code;
5641 1431 : code.reserve(64);
5642 1431 : compilemain(code, p, Value_Integer);
5643 : tagval result;
5644 1431 : runcode(code.data()+1, result);
5645 1431 : if(static_cast<int>(code[0]) >= 0x100)
5646 : {
5647 0 : uint * arr = new uint[code.size()];
5648 0 : std::memcpy(arr, code.data(), code.size()*sizeof(uint));
5649 : }
5650 1431 : int i = result.getint();
5651 1431 : freearg(result);
5652 1431 : return i;
5653 1431 : }
5654 :
5655 1 : int execute(ident *id, tagval *args, int numargs, bool lookup)
5656 : {
5657 : tagval result;
5658 1 : executeret(id, args, numargs, lookup, result);
5659 1 : int i = result.getint();
5660 1 : freearg(result);
5661 1 : return i;
5662 : }
5663 :
5664 1 : int execident(const char *name, int noid, bool lookup)
5665 : {
5666 1 : ident *id = nullptr;
5667 1 : std::unordered_map<std::string, ident>::iterator itr = idents.find(name);
5668 1 : if(itr != idents.end())
5669 : {
5670 1 : id = &(*(itr)).second;
5671 : }
5672 2 : return id ? execute(id, nullptr, 0, lookup) : noid;
5673 : }
5674 :
5675 86 : bool executebool(const uint *code)
5676 : {
5677 : tagval result;
5678 86 : runcode(code, result);
5679 86 : bool b = getbool(result);
5680 86 : freearg(result);
5681 86 : return b;
5682 : }
5683 :
5684 0 : bool executebool(ident *id, tagval *args, int numargs, bool lookup)
5685 : {
5686 : tagval result;
5687 0 : executeret(id, args, numargs, lookup, result);
5688 0 : bool b = getbool(result);
5689 0 : freearg(result);
5690 0 : return b;
5691 : }
5692 :
5693 0 : static void doargs(uint *body)
5694 : {
5695 0 : if(aliasstack != &noalias)
5696 : {
5697 0 : UNDOARGS
5698 0 : executeret(body, *commandret);
5699 0 : REDOARGS
5700 : }
5701 : else
5702 : {
5703 0 : executeret(body, *commandret);
5704 : }
5705 0 : }
5706 :
5707 : std::unordered_map<std::string, DefVar> defvars;
5708 :
5709 1 : void initcscmds()
5710 : {
5711 1 : addcommand("local", static_cast<identfun>(nullptr), nullptr, Id_Local);
5712 :
5713 2 : addcommand("defvar", reinterpret_cast<identfun>(+[] (const char *name, int *min, int *initval, int *max, char *onchange)
5714 : {
5715 1 : std::unordered_map<std::string, ident>::const_iterator itr = idents.find(name);
5716 1 : if(itr != idents.end())
5717 : {
5718 1 : debugcode("cannot redefine %s as a variable", name);
5719 1 : return;
5720 : }
5721 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5722 0 : DefVar &def = (*(insert.first)).second;
5723 0 : def.name = newstring(name);
5724 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5725 0 : def.i = variable(name, *min, *initval, *max, &def.i, def.onchange ? DefVar::changed : nullptr, 0);
5726 1 : }), "siiis", Id_Command);
5727 2 : addcommand("defvarp", reinterpret_cast<identfun>(+[] (const char *name, int *min, int *initval, int *max, char *onchange)
5728 : {
5729 1 : std::unordered_map<std::string, ident>::const_iterator itr = idents.find(name);
5730 1 : if(itr != idents.end())
5731 : {
5732 1 : debugcode("cannot redefine %s as a variable", name);
5733 1 : return;
5734 : }
5735 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5736 0 : DefVar &def = (*(insert.first)).second;
5737 0 : def.name = newstring(name);
5738 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5739 0 : def.i = variable(name, *min, *initval, *max, &def.i, def.onchange ? DefVar::changed : nullptr, Idf_Persist);
5740 1 : }), "siiis", Id_Command);
5741 2 : addcommand("deffvar", reinterpret_cast<identfun>(+[] (const char *name, float *min, float *initval, float *max, char *onchange)
5742 : {
5743 1 : std::unordered_map<std::string, ident>::const_iterator itr = idents.find(name);
5744 1 : if(itr != idents.end())
5745 : {
5746 1 : debugcode("cannot redefine %s as a variable", name);
5747 1 : return;
5748 : }
5749 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5750 0 : DefVar &def = (*(insert.first)).second;
5751 0 : def.name = newstring(name);
5752 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5753 0 : def.f = fvariable(name, *min, *initval, *max, &def.f, def.onchange ? DefVar::changed : nullptr, 0);
5754 1 : }), "sfffs", Id_Command);
5755 2 : addcommand("deffvarp", reinterpret_cast<identfun>(+[] (const char *name, float *min, float *initval, float *max, char *onchange)
5756 : {
5757 1 : std::unordered_map<std::string, ident>::const_iterator itr = idents.find(name);
5758 1 : if(itr != idents.end())
5759 : {
5760 1 : debugcode("cannot redefine %s as a variable", name);
5761 1 : return;
5762 : }
5763 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5764 0 : DefVar &def = (*(insert.first)).second;
5765 0 : def.name = newstring(name);
5766 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5767 0 : def.f = fvariable(name, *min, *initval, *max, &def.f, def.onchange ? DefVar::changed : nullptr, Idf_Persist);
5768 1 : }), "sfffs", Id_Command);
5769 2 : addcommand("defsvar", reinterpret_cast<identfun>(+[] (const char *name, char *initval, char *onchange)
5770 : {
5771 1 : std::unordered_map<std::string, ident>::const_iterator itr = idents.find(name);
5772 1 : if(itr != idents.end())
5773 : {
5774 1 : debugcode("cannot redefine %s as a variable", name);
5775 1 : return;
5776 : }
5777 0 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5778 0 : DefVar &def = (*(insert.first)).second;
5779 0 : def.name = newstring(name);
5780 0 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5781 0 : def.s = svariable(name, initval, &def.s, def.onchange ? DefVar::changed : nullptr, 0);
5782 1 : }), "sss", Id_Command);
5783 2 : addcommand("defsvarp", reinterpret_cast<identfun>(+[] (const char *name, char *initval, char *onchange)
5784 : {
5785 1 : std::unordered_map<std::string, ident>::const_iterator itr = idents.find(std::string(name));
5786 1 : if(itr != idents.end())
5787 : {
5788 0 : debugcode("cannot redefine %s as a variable", name); return;
5789 : }
5790 1 : auto insert = defvars.insert( { std::string(name), DefVar() } );
5791 1 : DefVar &def = (*(insert.first)).second;
5792 1 : def.name = newstring(name);
5793 1 : def.onchange = onchange[0] ? compilecode(onchange) : nullptr;
5794 1 : def.s = svariable(name, initval, &def.s, def.onchange ? DefVar::changed : nullptr, Idf_Persist);
5795 1 : }), "sss", Id_Command);
5796 2 : addcommand("getvarmin", reinterpret_cast<identfun>(+[] (const char *s) { intret(getvarmin(s)); }), "s", Id_Command);
5797 2 : addcommand("getfvarmin", reinterpret_cast<identfun>(+[] (const char *s) { floatret(getfvarmin(s)); }), "s", Id_Command);
5798 2 : addcommand("getfvarmax", reinterpret_cast<identfun>(+[] (const char *s) { floatret(getfvarmax(s)); }), "s", Id_Command);
5799 2 : addcommand("identexists", reinterpret_cast<identfun>(+[] (const char *s) { intret(identexists(s) ? 1 : 0); }), "s", Id_Command);
5800 2 : addcommand("getalias", reinterpret_cast<identfun>(+[] (const char *s) { result(getalias(s)); }), "s", Id_Command);
5801 :
5802 2 : addcommand("nodebug", reinterpret_cast<identfun>(+[] (uint *body){nodebug++; executeret(body, *commandret); nodebug--;}), "e", Id_Command);
5803 1 : addcommand("push", reinterpret_cast<identfun>(pushcmd), "rTe", Id_Command);
5804 2 : addcommand("alias", reinterpret_cast<identfun>(+[] (const char *name, tagval *v){ setalias(name, *v); v->type = Value_Null;}), "sT", Id_Command);
5805 1 : addcommand("resetvar", reinterpret_cast<identfun>(resetvar), "s", Id_Command);
5806 1 : addcommand("doargs", reinterpret_cast<identfun>(doargs), "e", Id_DoArgs);
5807 1 : }
|