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