LCOV - code coverage report
Current view: top level - engine/interface - command.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 48.2 % 2947 1421
Test Date: 2026-08-20 06:51:03 Functions: 71.0 % 169 120

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

Generated by: LCOV version 2.0-1