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