LCOV - code coverage report
Current view: top level - engine/interface - command.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 1400 2925 47.9 %
Date: 2024-11-22 05:07:59 Functions: 118 167 70.7 %

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

Generated by: LCOV version 1.14