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

Generated by: LCOV version 2.0-1