LCOV - code coverage report
Current view: top level - engine/interface - command.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 47.8 % 2933 1403
Test Date: 2025-02-18 06:21:28 Functions: 70.7 % 167 118

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

Generated by: LCOV version 2.0-1