LCOV - code coverage report
Current view: top level - asar - asar_math.cpp (source / functions) Coverage Total Hit
Test: asar.info Lines: 89.5 % 457 409
Test Date: 2024-01-15 17:20:38 Functions: 73.1 % 93 68
Branches: 65.0 % 428 278

             Branch data     Line data    Source code
       1                 :             : #include "platform/file-helpers.h"
       2                 :             : #include "asar.h"
       3                 :             : #include "virtualfile.h"
       4                 :             : #include "assembleblock.h"
       5                 :             : #include "macro.h"
       6                 :             : #include "asar_math.h"
       7                 :             : #include "table.h"
       8                 :             : #include "unicode.h"
       9                 :             : #include <cmath>
      10                 :             : #include <functional>
      11                 :             : #include <algorithm>
      12                 :             : 
      13                 :             : static const char * str;
      14                 :             : //save before calling eval if needed after
      15                 :             : static const char * current_user_function_name;
      16                 :             : 
      17                 :             : static double getnumcore();
      18                 :             : static double getnum();
      19                 :             : static double eval(int depth);
      20                 :             : 
      21                 :             : //label (bool foundLabel) (bool forwardLabel)
      22                 :             : //userfunction
      23                 :             : 
      24                 :             : bool foundlabel;
      25                 :             : bool foundlabel_static;
      26                 :             : bool forwardlabel;
      27                 :             : 
      28                 :             : struct cachedfile {
      29                 :             :         string filename;
      30                 :             :         virtual_file_handle filehandle;
      31                 :             :         size_t filesize;
      32                 :             :         bool used;
      33                 :             : };
      34                 :             : 
      35                 :             : #define numcachedfiles 16
      36                 :             : 
      37                 :             : static cachedfile cachedfiles[numcachedfiles];
      38                 :             : static int cachedfileindex = 0;
      39                 :             : 
      40                 :             : // Opens a file, trying to open it from cache first
      41                 :             : 
      42                 :          28 : static cachedfile * opencachedfile(string fname, bool should_error)
      43                 :             : {
      44                 :             :         cachedfile * cachedfilehandle = nullptr;
      45                 :             : 
      46                 :          28 :         const char* current_file = get_current_file_name();
      47                 :             : 
      48                 :             :         // RPG Hacker: Only using a combined path here because that should
      49                 :             :         // hopefully result in a unique string for every file, whereas
      50                 :             :         // fname could be a relative path, which isn't guaranteed to be unique.
      51                 :             :         // Note that this does not affect how we open the file - this is
      52                 :             :         // handled by the filesystem and uses our include paths etc.
      53                 :          28 :         string combinedname = filesystem->create_absolute_path(dir(current_file), fname);
      54                 :             : 
      55         [ +  + ]:         184 :         for (int i = 0; i < numcachedfiles; i++)
      56                 :             :         {
      57   [ +  +  +  + ]:         175 :                 if (cachedfiles[i].used && cachedfiles[i].filename == combinedname)
      58                 :             :                 {
      59                 :          19 :                         cachedfilehandle = &cachedfiles[i];
      60                 :             :                         break;
      61                 :             :                 }
      62                 :             :         }
      63                 :             : 
      64                 :             :         if (cachedfilehandle == nullptr)
      65                 :             :         {
      66         [ -  + ]:           9 :                 if (cachedfiles[cachedfileindex].used)
      67                 :             :                 {
      68                 :           0 :                         filesystem->close_file(cachedfiles[cachedfileindex].filehandle);
      69                 :           0 :                         cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
      70                 :           0 :                         cachedfiles[cachedfileindex].used = false;
      71                 :             :                 }
      72                 :             : 
      73                 :           9 :                 cachedfilehandle = &cachedfiles[cachedfileindex];
      74                 :             :         }
      75                 :             : 
      76                 :             :         if (cachedfilehandle != nullptr)
      77                 :             :         {
      78         [ +  + ]:          28 :                 if (!cachedfilehandle->used)
      79                 :             :                 {
      80                 :           9 :                         cachedfilehandle->filehandle = filesystem->open_file(fname, current_file);
      81         [ +  + ]:           9 :                         if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE)
      82                 :             :                         {
      83                 :           4 :                                 cachedfilehandle->used = true;
      84                 :           4 :                                 cachedfilehandle->filename = combinedname;
      85                 :           4 :                                 cachedfilehandle->filesize = filesystem->get_file_size(cachedfilehandle->filehandle);
      86                 :           4 :                                 cachedfileindex++;
      87                 :             :                                 // randomdude999: when we run out of cached files, just start overwriting ones from the start
      88         [ -  + ]:           4 :                                 if (cachedfileindex >= numcachedfiles) cachedfileindex = 0;
      89                 :             :                         }
      90                 :             :                 }
      91                 :             :         }
      92                 :             : 
      93   [ +  +  -  + ]:          28 :         if ((cachedfilehandle == nullptr || cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && should_error)
      94                 :             :         {
      95                 :           0 :                 asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), fname.data());
      96                 :             :         }
      97                 :             : 
      98                 :          28 :         return cachedfilehandle;
      99                 :          28 : }
     100                 :             : 
     101                 :             : 
     102                 :         230 : void closecachedfiles()
     103                 :             : {
     104         [ +  + ]:        3910 :         for (int i = 0; i < numcachedfiles; i++)
     105                 :             :         {
     106         [ +  + ]:        3680 :                 if (cachedfiles[i].used)
     107                 :             :                 {
     108   [ +  -  +  - ]:           4 :                         if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE && filesystem)
     109                 :             :                         {
     110                 :           4 :                                 filesystem->close_file(cachedfiles[i].filehandle);
     111                 :           4 :                                 cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
     112                 :             :                         }
     113                 :             : 
     114                 :           4 :                         cachedfiles[i].used = false;
     115                 :             :                 }
     116                 :             :         }
     117                 :             : 
     118                 :         230 :         cachedfileindex = 0;
     119                 :         230 : }
     120                 :             : 
     121                 :             : 
     122                 :           3 : static int struct_size(const char *name)
     123                 :             : {
     124   [ +  -  -  + ]:           3 :        if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
     125         [ +  - ]:           3 :        else if(!structs.exists(name)) return 0;
     126                 :           3 :        return structs.find(name).struct_size;
     127                 :             : }
     128                 :             : 
     129                 :          19 : static int object_size(const char *name)
     130                 :             : {
     131   [ +  +  -  + ]:          19 :        if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
     132         [ +  - ]:          19 :        else if(!structs.exists(name)) return 0;
     133                 :          19 :        return structs.find(name).object_size;
     134                 :             : }
     135                 :             : 
     136                 :           5 : static int data_size(const char *name)
     137                 :             : {
     138                 :             :         int label;
     139         [ -  + ]:           5 :         if(!labels.exists(name)) asar_throw_error(2, error_type_block, error_id_label_not_found, name);
     140                 :           5 :         foundlabel = true;
     141                 :           5 :         snes_label label_data = labels.find(name);
     142                 :           5 :         foundlabel_static &= label_data.is_static;
     143                 :             :         label = label_data.id;
     144                 :           5 :         snes_label selected_label;
     145                 :           5 :         selected_label.id = 0xFFFFFF;
     146                 :           5 :         selected_label.pos = 0xFFFFFF;
     147                 :           5 :         labels.each([&selected_label, label](const char *key, snes_label current_label){
     148   [ +  +  +  + ]:          15 :                 if(label < current_label.id && current_label.id < selected_label.id){
     149                 :           3 :                         selected_label = current_label;
     150                 :             :                 }
     151                 :          15 :         });
     152         [ +  + ]:           5 :         if(selected_label.id == 0xFFFFFF) asar_throw_warning(2, warning_id_datasize_last_label, name);
     153         [ +  + ]:           5 :         if(selected_label.pos-label_data.pos > 0xFFFF) asar_throw_warning(2, warning_id_datasize_exceeds_size, name);
     154                 :           5 :         return selected_label.pos-label_data.pos;
     155                 :             : }
     156                 :             : 
     157                 :             : 
     158                 :          85 : string get_string_argument()
     159                 :             : {
     160         [ -  + ]:          85 :         while (*str==' ') str++;
     161         [ +  - ]:          85 :         if (*str=='"')
     162                 :             :         {
     163                 :          85 :                 const char * strpos = str + 1;
     164                 :             :                 while (*str!='"' && *str!='\0') str++;
     165                 :          85 :                 str = strchr(str + 1, '"');
     166                 :          85 :                 string tempname(strpos , (int)(str - strpos));
     167                 :          85 :                 str++;
     168         [ -  + ]:          85 :                 while (*str==' ') str++;        //eat space
     169                 :          85 :                 return tempname;
     170                 :          85 :         }//todo make this error a better one later
     171                 :           0 :         asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
     172                 :           0 :         return ""; //never actually called, but I don't feel like figuring out __attribute__ ((noreturn)) on MSVC
     173                 :             : }
     174                 :             : 
     175                 :             : //only returns alphanumeric (and _) starting with alpha or _
     176                 :         186 : string get_symbol_argument()
     177                 :             : {
     178         [ -  + ]:         186 :         while (*str==' ') str++;        //is this proper?  Dunno yet.
     179                 :             :         const char * strpos = str;
     180         [ +  + ]:         186 :         if(is_ualpha(*str)) str++;
     181   [ +  +  +  + ]:         790 :         while (is_ualnum(*str) || *str == '.') str++;
     182         [ -  + ]:         186 :         if(strpos == str){
     183                 :             :                 //error nothing was read, this is a placeholder error
     184                 :           0 :                 asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
     185                 :             :         }
     186                 :             : 
     187                 :         186 :         string symbol = string(strpos, (int)(str - strpos));
     188         [ -  + ]:         186 :         while (*str==' ') str++;        //eat spaces
     189                 :         186 :         return symbol;
     190                 :             : }
     191                 :             : 
     192                 :         212 : double get_double_argument()
     193                 :             : {
     194         [ -  + ]:         212 :         while (*str==' ') str++;
     195                 :         212 :         double result = eval(0);
     196         [ -  + ]:         212 :         while (*str==' ') str++; //eat spaces
     197                 :         212 :         return result;
     198                 :             : }
     199                 :             : 
     200                 :             : //will eat the comma
     201                 :          36 : bool has_next_parameter()
     202                 :             : {
     203         [ +  + ]:          36 :         if (*str==',')
     204                 :             :         {
     205                 :          12 :                 str++;
     206                 :          12 :                 return true;
     207                 :             :         }
     208                 :             :         return false;
     209                 :             : }
     210                 :             : 
     211                 :          95 : void require_next_parameter()
     212                 :             : {
     213         [ +  - ]:          95 :         if (*str==',')
     214                 :             :         {
     215                 :          95 :                 str++;
     216                 :          95 :                 return;
     217                 :             :         }
     218                 :           0 :         asar_throw_error(2, error_type_block, error_id_require_parameter);
     219                 :             : }
     220                 :             : 
     221                 :           2 : template <typename F> double asar_unary_wrapper()
     222                 :             : {
     223                 :           2 :         return F()(get_double_argument());
     224                 :             : }
     225                 :             : 
     226                 :           1 : template <double (*F)(double)> double asar_unary_wrapper()
     227                 :             : {
     228                 :           1 :         return F(get_double_argument());
     229                 :             : }
     230                 :             : 
     231                 :             : //possibly restrict type T in the future....
     232                 :             : //first a case for functors
     233                 :          22 : template <typename F> double asar_binary_wrapper()
     234                 :             : {
     235                 :          22 :         double first = get_double_argument();
     236                 :          22 :         require_next_parameter();
     237                 :          22 :         return F()(first, get_double_argument());
     238                 :             : }
     239                 :             : //this could be DRY with if constexpr....oh well
     240                 :          13 : template <double (*F)(double, double)> double asar_binary_wrapper()
     241                 :             : {
     242                 :          13 :         double first = get_double_argument();
     243                 :          13 :         require_next_parameter();
     244                 :          13 :         return F(first, get_double_argument());
     245                 :             : }
     246                 :             : 
     247                 :           0 : double asar_bank(double a)
     248                 :             : {
     249                 :           1 :         return (int)a >> 16;
     250                 :             : }
     251                 :             : 
     252                 :             : 
     253                 :           0 : double asar_logical_nand(double a, double b)
     254                 :             : {
     255                 :           3 :         return !(a && b);
     256                 :             : }
     257                 :             : 
     258                 :             : 
     259                 :           0 : double asar_logical_nor(double a, double b)
     260                 :             : {
     261                 :           3 :         return !(a || b);
     262                 :             : }
     263                 :             : 
     264                 :             : 
     265                 :           0 : double asar_logical_xor(double a, double b)
     266                 :             : {
     267                 :           3 :         return !!a ^ !!b;
     268                 :             : }
     269                 :             : 
     270                 :           0 : double asar_max(double a, double b)
     271                 :             : {
     272   [ +  +  +  +  :           5 :         return a > b ? a : b;
                   -  - ]
     273                 :             : }
     274                 :             : 
     275                 :           0 : double asar_min(double a, double b)
     276                 :             : {
     277   [ +  +  +  +  :           5 :         return a < b ? a : b;
                   -  - ]
     278                 :             : }
     279                 :             : 
     280                 :           3 : double asar_clamp()
     281                 :             : {
     282                 :           3 :         double value = get_double_argument();
     283                 :           3 :         require_next_parameter();
     284                 :           3 :         double low = get_double_argument();
     285                 :           3 :         require_next_parameter();
     286                 :           3 :         double high = get_double_argument();
     287                 :             : 
     288                 :           3 :         return asar_max(low, asar_min(high, value));
     289                 :             : }
     290                 :             : 
     291                 :           2 : double asar_safediv()
     292                 :             : {
     293                 :           2 :         double dividend = get_double_argument();
     294                 :           2 :         require_next_parameter();
     295                 :           2 :         double divisor = get_double_argument();
     296                 :           2 :         require_next_parameter();
     297                 :           2 :         double default_value = get_double_argument();
     298                 :             : 
     299         [ +  + ]:           2 :         return divisor == 0.0 ? default_value : dividend / divisor;
     300                 :             : }
     301                 :             : 
     302                 :           8 : double asar_select()
     303                 :             : {
     304                 :           8 :         double selector = get_double_argument();
     305                 :           8 :         require_next_parameter();
     306                 :           8 :         double a = get_double_argument();
     307                 :           8 :         require_next_parameter();
     308                 :           8 :         double b = get_double_argument();
     309                 :             : 
     310         [ +  + ]:           8 :         return selector == 0.0 ? b : a;
     311                 :             : }
     312                 :             : 
     313                 :          29 : double asar_snestopc_wrapper()
     314                 :             : {
     315                 :          29 :         return snestopc(get_double_argument());
     316                 :             : }
     317                 :             : 
     318                 :          21 : double asar_pctosnes_wrapper()
     319                 :             : {
     320                 :          21 :         return pctosnes(get_double_argument());
     321                 :             : }
     322                 :             : 
     323                 :           3 : double asar_realbase_wrapper()
     324                 :             : {
     325                 :             :         //need a better way to do this to make sure it is useful...
     326                 :             :         //foundlabel=true;  //Going to consider this an implicit label because we don't really have a better system
     327                 :           3 :         return realsnespos;
     328                 :             : }
     329                 :             : 
     330                 :           5 : template <int count> double asar_read()
     331                 :             : {
     332                 :           5 :         int target = get_double_argument();
     333                 :           5 :         int addr=snestopc_pick(target);
     334         [ -  + ]:           5 :         if(has_next_parameter())
     335                 :             :         {
     336                 :           0 :                 double default_value = get_double_argument();
     337         [ #  # ]:           0 :                 if (addr<0) return default_value;
     338         [ #  # ]:           0 :                 else if (addr+count>romlen_r) return default_value;
     339                 :             :         }
     340                 :             :         else
     341                 :             :         {
     342   [ -  +  -  - ]:           5 :                 if (addr<0) asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, (hex((unsigned int)target, 6) + " in read function").data());
     343   [ +  +  -  + ]:           7 :                 else if (addr+count>romlen_r) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, (hex(target, 6) + " in read function").data());
     344                 :             :         }
     345                 :             : 
     346                 :             :         unsigned int value = 0;
     347         [ +  + ]:          14 :         for(int i = 0; i < count; i++)
     348                 :             :         {
     349                 :          10 :                 value |= romdata_r[addr+i] << (8 * i);
     350                 :             :         }
     351                 :           4 :         return value;
     352                 :             : }
     353                 :             : 
     354                 :          12 : template <int count> double asar_canread()
     355                 :             : {
     356                 :             :         int length = count;
     357                 :             :         if(!length)
     358                 :             :         {
     359                 :           0 :                 length = get_double_argument();
     360                 :             :         }
     361                 :          12 :         int addr=snestopc_pick(get_double_argument());
     362   [ +  -  +  + ]:          12 :         if (addr<0 || addr+length-1>=romlen_r) return 0;
     363                 :           6 :         else return 1;
     364                 :             : }
     365                 :             : 
     366                 :          14 : template <size_t count> double asar_readfile()
     367                 :             : {
     368                 :             :         static_assert(count && count <= 4, "invalid count"); //1-4 inclusive
     369                 :             : 
     370                 :          14 :         string name = get_string_argument();
     371                 :          14 :         require_next_parameter();
     372                 :          14 :         size_t offset = get_double_argument();
     373                 :          14 :         bool should_error = !has_next_parameter();
     374                 :          14 :         cachedfile * fhandle = opencachedfile(name, should_error);
     375         [ +  + ]:          14 :         if(!should_error)
     376                 :             :         {
     377                 :           6 :                 double default_value = get_double_argument();
     378   [ +  -  +  + ]:           6 :                 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return default_value;
     379         [ +  + ]:           5 :                 if (offset < 0 || offset + count > fhandle->filesize) return default_value;
     380                 :             :         }
     381                 :             :         else
     382                 :             :         {
     383   [ +  -  -  +  :           8 :                 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
          -  -  -  -  -  
                      - ]
     384   [ -  +  -  - ]:           8 :                 if (offset < 0 || offset + count > fhandle->filesize) asar_throw_error(2, error_type_block, error_id_file_offset_out_of_bounds, dec(offset).data(), name.data());
     385                 :             :         }
     386                 :             : 
     387                 :          11 :         unsigned char data[4] = { 0, 0, 0, 0 };
     388                 :          11 :         filesystem->read_file(fhandle->filehandle, data, offset, count);
     389                 :             : 
     390                 :             :         unsigned int value = 0;
     391         [ +  + ]:          29 :         for(size_t i = 0; i < count; i++)
     392                 :             :         {
     393                 :          18 :                 value |= data[i] << (8 * i);
     394                 :             :         }
     395                 :             : 
     396                 :          11 :         return value;
     397                 :          14 : }
     398                 :             : 
     399                 :           7 : template <size_t count> double asar_canreadfile()
     400                 :             : {
     401                 :           7 :         string name = get_string_argument();
     402                 :           7 :         require_next_parameter();
     403                 :           7 :         size_t offset = get_double_argument();
     404                 :             :         size_t length = count;
     405                 :             :         if(!count)
     406                 :             :         {
     407                 :           2 :                 require_next_parameter();
     408                 :           2 :                 length = get_double_argument();
     409                 :             :         }
     410                 :           7 :         cachedfile * fhandle = opencachedfile(name, false);
     411   [ +  -  +  + ]:           7 :         if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return 0;
     412         [ +  + ]:           6 :         if (offset < 0 || offset + length > fhandle->filesize) return 0;
     413                 :             :         return 1;
     414                 :           7 : }
     415                 :             : 
     416                 :             : // returns 0 if the file is OK, 1 if the file doesn't exist, 2 if it couldn't be opened for some other reason
     417                 :           6 : static double asar_filestatus()
     418                 :             : {
     419                 :           6 :         cachedfile * fhandle = opencachedfile(get_string_argument(), false);
     420   [ +  -  +  + ]:           6 :         if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
     421                 :             :         {
     422         [ -  + ]:           3 :                 if (filesystem->get_last_error() == vfe_doesnt_exist)
     423                 :             :                 {
     424                 :             :                         return 1;
     425                 :             :                 }
     426                 :             :                 else
     427                 :             :                 {
     428                 :           0 :                         return 2;
     429                 :             :                 }
     430                 :             :         }
     431                 :             :         return 0;
     432                 :             : }
     433                 :             : 
     434                 :             : // Returns the size of the specified file.
     435                 :           1 : static double asar_filesize()
     436                 :             : {
     437                 :           1 :         string name = get_string_argument();
     438                 :           1 :         cachedfile * fhandle = opencachedfile(name, false);
     439   [ +  -  -  +  :           1 :         if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
          -  -  -  -  -  
                      - ]
     440                 :           1 :         return (double)fhandle->filesize;
     441                 :           1 : }
     442                 :             : 
     443                 :             : // Checks whether the specified define is defined.
     444                 :          37 : static double asar_isdefined()
     445                 :             : {
     446                 :          37 :         return defines.exists(get_string_argument());
     447                 :             : }
     448                 :             : 
     449                 :             : // RPG Hacker: What exactly makes this function overly complicated, you ask?
     450                 :             : // Well, it converts a double to a string and then back to a double.
     451                 :             : // It was the quickest reliable solution I could find, though, so there's that.
     452                 :           2 : static double asar_round()
     453                 :             : {
     454                 :           2 :         double number = get_double_argument();
     455                 :           2 :         require_next_parameter();
     456                 :             : 
     457                 :             :         // Hue hue hue... ass!
     458                 :             :         // OK, sorry, I apologize.
     459                 :           2 :         string asstring = ftostrvar(number, get_double_argument());
     460                 :             : 
     461                 :             :         // Some hacky shenanigans with variables going on here
     462                 :           2 :         const char * strbackup = str;
     463                 :           2 :         str = asstring;
     464                 :           2 :         double asdouble = (double)getnum();
     465                 :           2 :         str = strbackup;
     466                 :             : 
     467                 :           2 :         return asdouble;
     468                 :           2 : }
     469                 :             : 
     470                 :         178 : static double asar_structsize_wrapper()
     471                 :             : {
     472                 :         178 :         string symbol = get_symbol_argument();
     473         [ +  + ]:         178 :         if(symbol == "..."){
     474   [ +  +  -  + ]:         175 :                 if(!inmacro) asar_throw_error(2, error_type_block, error_id_vararg_sizeof_nomacro);
     475   [ +  +  -  + ]:         174 :                 if(numvarargs == -1) asar_throw_error(2, error_type_block, error_id_macro_not_varadic, "sizeof(...)");
     476                 :         173 :                 return numvarargs;
     477                 :             :         }
     478                 :           3 :         return (double)struct_size(symbol);
     479                 :         178 : }
     480                 :             : 
     481                 :           3 : static double asar_objectsize_wrapper()
     482                 :             : {
     483                 :           3 :         return (double)object_size(get_symbol_argument());
     484                 :             : }
     485                 :             : 
     486                 :           5 : static double asar_datasize_wrapper()
     487                 :             : {
     488                 :           5 :         return (double)data_size(get_symbol_argument());
     489                 :             : }
     490                 :             : 
     491                 :           6 : static double asar_stringsequal()
     492                 :             : {
     493                 :           6 :         string string1 = get_string_argument();
     494                 :           6 :         require_next_parameter();
     495   [ +  -  +  + ]:          15 :         return (strcmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0);
     496                 :           6 : }
     497                 :             : 
     498                 :           3 : static double asar_stringsequalnocase()
     499                 :             : {
     500                 :           3 :         string string1 = get_string_argument();
     501                 :           3 :         require_next_parameter();
     502   [ +  -  -  + ]:           6 :         return (stricmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0);
     503                 :           3 : }
     504                 :             : 
     505                 :           0 : static double asar_char()
     506                 :             : {
     507                 :           0 :         string string1 = get_string_argument();
     508                 :           0 :         require_next_parameter();
     509                 :           0 :         int offset = get_double_argument();
     510   [ #  #  #  # ]:           0 :         if(offset >= string1.length()) asar_throw_error(2, error_type_block, error_id_oob, offset, string1.length());
     511                 :           0 :         return string1[offset];
     512                 :           0 : }
     513                 :             : 
     514                 :           0 : static double asar_strlen()
     515                 :             : {
     516                 :           0 :         return get_string_argument().length();
     517                 :             : }
     518                 :             : 
     519                 :          17 : string copy_arg()
     520                 :             : {
     521         [ +  + ]:          17 :         if(*str == '"')
     522                 :             :         {
     523                 :           2 :                 string t = "\"";
     524                 :           4 :                 return (t += get_string_argument() + "\"");
     525                 :           2 :         }
     526                 :             : 
     527                 :          15 :         string result;
     528                 :             :         bool is_symbolic = true;
     529                 :             :         int parlevel=0;
     530                 :             :         int i = 0;
     531   [ -  +  +  +  :          30 :         while(parlevel > 0 || (str[i] != ',' && str[i] != ')'))
                   +  + ]
     532                 :             :         {
     533                 :          15 :                 is_symbolic &= is_ualnum(str[i]);
     534         [ -  + ]:          15 :                 if(str[i] == '(') parlevel++;
     535         [ -  + ]:          15 :                 else if(str[i] == ')') parlevel--;
     536                 :          15 :                 i++;
     537                 :             :         }
     538                 :          15 :         result += string(str, i);
     539                 :          15 :         str += i;
     540                 :             : 
     541         [ -  + ]:          15 :         if(!is_symbolic)
     542                 :             :         {
     543                 :             :                 const char *oldstr=str;
     544                 :           0 :                 str = (const char *)result;
     545                 :           0 :                 result = ftostr(eval(0));
     546                 :           0 :                 str = oldstr;
     547                 :             :         }
     548                 :          15 :         return result;
     549                 :          15 : }
     550                 :             : 
     551                 :             : assocarr<double (*)()> builtin_functions =
     552                 :             : {
     553                 :             :         {"sqrt", asar_unary_wrapper<sqrt>},
     554                 :             :         {"sin", asar_unary_wrapper<sin>},
     555                 :             :         {"cos", asar_unary_wrapper<cos>},
     556                 :             :         {"tan", asar_unary_wrapper<tan>},
     557                 :             :         {"asin", asar_unary_wrapper<asin>},
     558                 :             :         {"acos", asar_unary_wrapper<acos>},
     559                 :             :         {"atan", asar_unary_wrapper<atan>},
     560                 :             :         {"arcsin", asar_unary_wrapper<asin>},
     561                 :             :         {"arccos", asar_unary_wrapper<acos>},
     562                 :             :         {"arctan", asar_unary_wrapper<atan>},
     563                 :             :         {"log", asar_unary_wrapper<log>},
     564                 :             :         {"log10", asar_unary_wrapper<log10>},
     565                 :             :         {"log2", asar_unary_wrapper<log2>},
     566                 :             : 
     567                 :             :         {"ceil", asar_unary_wrapper<ceil>},
     568                 :             :         {"floor", asar_unary_wrapper<floor>},
     569                 :             : 
     570                 :             :         {"read1", asar_read<1>},        //This handles the safe and unsafe variant
     571                 :             :         {"read2", asar_read<2>},
     572                 :             :         {"read3", asar_read<3>},
     573                 :             :         {"read4", asar_read<4>},
     574                 :             :         {"canread", asar_canread<0>},
     575                 :             :         {"canread1", asar_canread<1>},
     576                 :             :         {"canread2", asar_canread<2>},
     577                 :             :         {"canread3", asar_canread<3>},
     578                 :             :         {"canread4", asar_canread<4>},
     579                 :             : 
     580                 :             :         {"readfile1", asar_readfile<1>},
     581                 :             :         {"readfile2", asar_readfile<2>},
     582                 :             :         {"readfile3", asar_readfile<3>},
     583                 :             :         {"readfile4", asar_readfile<4>},
     584                 :             :         {"canreadfile", asar_canreadfile<0>},
     585                 :             :         {"canreadfile1", asar_canreadfile<1>},
     586                 :             :         {"canreadfile2", asar_canreadfile<2>},
     587                 :             :         {"canreadfile3", asar_canreadfile<3>},
     588                 :             :         {"canreadfile4", asar_canreadfile<4>},
     589                 :             : 
     590                 :             :         {"filesize", asar_filesize},
     591                 :             :         {"getfilestatus", asar_filestatus},
     592                 :             : 
     593                 :             :         {"defined", asar_isdefined},
     594                 :             : 
     595                 :             :         {"snestopc", asar_snestopc_wrapper},
     596                 :             :         {"pctosnes", asar_pctosnes_wrapper},
     597                 :             :         {"realbase", asar_realbase_wrapper},
     598                 :             : 
     599                 :             :         {"max", asar_binary_wrapper<asar_max>},
     600                 :             :         {"min", asar_binary_wrapper<asar_min>},
     601                 :             :         {"clamp", asar_clamp},
     602                 :             : 
     603                 :             :         {"safediv", asar_safediv},
     604                 :             : 
     605                 :             :         {"select", asar_select},
     606                 :             :         {"bank", asar_unary_wrapper<asar_bank>},
     607                 :             :         {"not", asar_unary_wrapper<std::logical_not<unsigned int>>},
     608                 :             :         {"equal", asar_binary_wrapper<std::equal_to<double>>},
     609                 :             :         {"notequal", asar_binary_wrapper<std::not_equal_to<double>>},
     610                 :             :         {"less", asar_binary_wrapper<std::less<double>>},
     611                 :             :         {"lessequal", asar_binary_wrapper<std::less_equal<double>>},
     612                 :             :         {"greater", asar_binary_wrapper<std::greater<double>>},
     613                 :             :         {"greaterequal", asar_binary_wrapper<std::greater_equal<double>>},
     614                 :             : 
     615                 :             :         {"and", asar_binary_wrapper<std::logical_and<unsigned int>>},
     616                 :             :         {"or", asar_binary_wrapper<std::logical_or<unsigned int>>},
     617                 :             :         {"nand", asar_binary_wrapper<asar_logical_nand>},
     618                 :             :         {"nor", asar_binary_wrapper<asar_logical_nor>},
     619                 :             :         {"xor", asar_binary_wrapper<asar_logical_xor>},
     620                 :             : 
     621                 :             :         {"round", asar_round},
     622                 :             : 
     623                 :             :         {"sizeof", asar_structsize_wrapper},
     624                 :             :         {"objectsize", asar_objectsize_wrapper},
     625                 :             :         {"datasize", asar_datasize_wrapper},
     626                 :             : 
     627                 :             :         {"stringsequal", asar_stringsequal},
     628                 :             :         {"stringsequalnocase", asar_stringsequalnocase},
     629                 :             :         {"char", asar_char},
     630                 :             :         {"stringlength", asar_strlen}
     631                 :             : };
     632                 :             : 
     633                 :             : assocarr<double (*)()> functions;
     634                 :             : 
     635                 :             : struct funcdat {
     636                 :             :         autoptr<char*> name;
     637                 :             :         int numargs;
     638                 :             :         autoptr<char*> argbuf;//this one isn't used, it's just to free it up
     639                 :             :         autoptr<char**> arguments;
     640                 :             :         autoptr<char*> content;
     641                 :             : };
     642                 :             : static assocarr<funcdat> user_functions;
     643                 :             : 
     644                 :          11 : static double asar_call_user_function()
     645                 :             : {
     646                 :          11 :         autoarray<string> args;
     647                 :          11 :         funcdat &user_function = user_functions[current_user_function_name];
     648                 :          11 :         string real_content;
     649                 :             : 
     650         [ -  + ]:          11 :         while (*str==' ') str++;
     651                 :          11 :         bool has_next = *str != ')';
     652                 :             : 
     653         [ +  + ]:          28 :         for (int i=0;i<user_function.numargs;i++)
     654                 :             :         {
     655         [ -  + ]:          17 :                 if(!has_next)
     656                 :             :                 {
     657                 :           0 :                         asar_throw_error(2, error_type_block, error_id_expected_parameter, current_user_function_name);
     658                 :             :                 }
     659                 :          34 :                 args[i] = copy_arg();
     660                 :          17 :                 has_next = has_next_parameter();
     661                 :             :         }
     662                 :             : 
     663         [ -  + ]:          11 :         if(has_next)
     664                 :             :         {
     665                 :           0 :                 asar_throw_error(2, error_type_block, error_id_unexpected_parameter, current_user_function_name);
     666                 :             :         }
     667                 :             : 
     668         [ +  + ]:          53 :         for(int i=0; user_function.content[i]; i++)
     669                 :             :         {
     670         [ +  + ]:          42 :                 if(!is_ualpha(user_function.content[i]))
     671                 :             :                 {
     672                 :          23 :                         real_content += user_function.content[i];
     673                 :          23 :                         continue;
     674                 :             :                 }
     675                 :             :                 bool found = false;
     676         [ +  + ]:          52 :                 for (int j=0;user_function.arguments[j];j++)
     677                 :             :                 {
     678                 :             :                         //this should *always* have a null term or another character after
     679                 :          33 :                         bool potential_arg = stribegin(user_function.content+i, user_function.arguments[j]);
     680                 :          33 :                         int next_char = i+strlen(user_function.arguments[j]);
     681   [ +  +  +  - ]:          33 :                         if(potential_arg && !is_ualnum(user_function.content[next_char]))
     682                 :             :                         {
     683                 :          17 :                                 real_content += args[j];
     684                 :          17 :                                 i = next_char - 1;
     685                 :             :                                 found = true;
     686                 :             :                         }
     687                 :             :                 }
     688                 :             : 
     689         [ +  + ]:          19 :                 if(!found){
     690         [ +  + ]:          20 :                         for(; is_ualnum(user_function.content[i]); i++){
     691                 :          18 :                                 real_content += user_function.content[i];
     692                 :             :                         }
     693                 :           2 :                         real_content += user_function.content[i];
     694                 :             :                 }
     695                 :             :         }
     696                 :          11 :         const char * oldstr=str;
     697                 :          11 :         str = (const char *)real_content;
     698                 :          11 :         double result = eval(0);
     699                 :          11 :         str = oldstr;
     700                 :          11 :         return result;
     701                 :          11 : }
     702                 :             : 
     703                 :          18 : void createuserfunc(const char * name, const char * arguments, const char * content)
     704                 :             : {
     705         [ -  + ]:          18 :         if (!confirmqpar(content)) asar_throw_error(0, error_type_block, error_id_mismatched_parentheses);
     706         [ -  + ]:          18 :         if(functions.exists(name)) //functions holds both types.
     707                 :             :         {
     708                 :           0 :                 asar_throw_error(0, error_type_block, error_id_function_redefined, name);
     709                 :             :         }
     710                 :          18 :         funcdat& user_function=user_functions[name];
     711                 :          18 :         user_function.name= duplicate_string(name);
     712                 :          18 :         user_function.argbuf= duplicate_string(arguments);
     713                 :          18 :         user_function.arguments=split(user_function.argbuf, ',', &(user_function.numargs));
     714                 :          18 :         user_function.content= duplicate_string(content);
     715         [ +  + ]:          45 :         for (int i=0;user_function.arguments[i];i++)
     716                 :             :         {
     717         [ +  + ]:          72 :                 for(int j=0;user_function.arguments[j];j++)
     718                 :             :                 {
     719   [ +  +  -  + ]:          45 :                         if(i!=j && !stricmp(user_function.arguments[i], user_function.arguments[j]))
     720                 :             :                         {
     721                 :           0 :                                 asar_throw_error(0, error_type_block, error_id_duplicate_param_name, user_function.arguments[i], name);
     722                 :             :                         }
     723                 :             :                 }
     724         [ -  + ]:          27 :                 if (!confirmname(user_function.arguments[i]))
     725                 :             :                 {
     726                 :           0 :                         user_functions.remove(name);
     727                 :           0 :                         asar_throw_error(0, error_type_block, error_id_invalid_param_name);
     728                 :             :                 }
     729                 :             :         }
     730                 :             : 
     731                 :          18 :         functions[name] = asar_call_user_function;
     732                 :          18 : }
     733                 :             : 
     734                 :             : inline const long hextable[] = {
     735                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     736                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     737                 :             :         -1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,
     738                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     739                 :             :         -1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     740                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     741                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     742                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     743                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     744                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
     745                 :             :         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
     746                 :             : };
     747                 :             : 
     748                 :        8512 : static double getnumcore()
     749                 :             : {
     750         [ +  + ]:        8512 :         if (*str=='$')
     751                 :             :         {
     752         [ -  + ]:        2318 :                 if (!is_xdigit(*++str)) asar_throw_error(2, error_type_block, error_id_invalid_hex_value);
     753                 :             :                 uint64_t ret = 0;
     754         [ +  + ]:       10546 :                 while (hextable[0 + *str] >= 0) {
     755                 :        8228 :                         ret = (ret << 4) | hextable[0 + *str++];
     756                 :             :                 }
     757                 :        2318 :                 return ret;
     758                 :             :         }
     759   [ +  +  +  +  :        6194 :         if (is_ualpha(*str) || *str=='.' || *str=='?')
                   +  + ]
     760                 :             :         {
     761                 :         626 :                 const char * start=str;
     762   [ +  +  +  + ]:        5552 :                 while (is_ualnum(*str) || *str == '.') str++;
     763                 :         626 :                 int len=(int)(str-start);
     764         [ +  + ]:         644 :                 while (*str==' ') str++;
     765         [ +  + ]:         626 :                 if (*str=='(')
     766                 :             :                 {
     767                 :         394 :                         str++;
     768                 :             :                         // RPG Hacker: This is only here to assure that all strings are still
     769                 :             :                         // alive in memory when we call our functions further down
     770                 :             :                         double result;
     771                 :             :                         while (true)
     772                 :             :                         {
     773         [ -  + ]:         394 :                                 while (*str==' ') str++;
     774                 :         394 :                                 string function_name = string(start, len);
     775         [ +  - ]:         394 :                                 if(functions.exists(function_name))
     776                 :             :                                 {
     777                 :         394 :                                         current_user_function_name = function_name;
     778                 :         394 :                                         result = functions[function_name]();
     779                 :             :                                 }
     780                 :             :                                 else
     781                 :             :                                 {
     782                 :           0 :                                         str++;
     783                 :             :                                         break;
     784                 :             :                                 }
     785                 :             : 
     786         [ +  - ]:         391 :                                 if (*str==')')
     787                 :             :                                 {
     788                 :         391 :                                         str++;
     789                 :             :                                         return result;
     790                 :             :                                 }
     791                 :           0 :                                 asar_throw_error(2, error_type_block, error_id_malformed_function_call);
     792                 :         394 :                         }
     793                 :             : 
     794                 :           0 :                         asar_throw_error(2, error_type_block, error_id_function_not_found, start);
     795                 :             :                 }
     796                 :             :                 else
     797                 :             :                 {
     798                 :         232 :                         foundlabel=true;
     799                 :             : 
     800                 :             :                         const char *old_start = start;
     801                 :         232 :                         snes_label label_data = labelval(&start);
     802                 :         231 :                         int i=(int)label_data.pos;
     803                 :         231 :                         foundlabel_static &= label_data.is_static;
     804         [ +  + ]:         231 :                         if(str < start)
     805                 :             :                         {
     806         [ +  + ]:          19 :                                 if ((str = strchr(str, '[')))
     807                 :             :                                 {
     808                 :          16 :                                         string struct_name = substr(old_start, (int)(str - old_start));
     809                 :          16 :                                         str++;
     810                 :          16 :                                         i += (int)(eval(0) * object_size(struct_name));
     811                 :          16 :                                 }
     812                 :             :                         }
     813                 :             : 
     814                 :         231 :                         str=start;
     815         [ +  + ]:         231 :                         if (i==-1) forwardlabel=true;
     816                 :         231 :                         return (int)i&0xFFFFFF;
     817                 :             :                 }
     818                 :             :         }
     819         [ +  + ]:        5568 :         if (*str=='(')
     820                 :             :         {
     821                 :          73 :                 str++;
     822                 :          73 :                 double rval=eval(0);
     823         [ -  + ]:          73 :                 if (*str != ')') asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
     824                 :          73 :                 str++;
     825                 :          73 :                 return rval;
     826                 :             :         }
     827         [ +  + ]:        5495 :         if (*str=='%')
     828                 :             :         {
     829         [ -  + ]:          12 :                 if (str[1] != '0' && str[1] != '1') asar_throw_error(2, error_type_block, error_id_invalid_binary_value);
     830                 :          12 :                 return strtoull(str+1, const_cast<char**>(&str), 2);
     831                 :             :         }
     832         [ +  + ]:        5483 :         if (*str=='\'')
     833                 :             :         {
     834         [ -  + ]:         110 :                 if (!str[1]) asar_throw_error(2, error_type_block, error_id_invalid_character);
     835                 :             :                 int orig_val;
     836                 :         110 :                 str++;
     837                 :         110 :                 str += utf8_val(&orig_val, str);
     838         [ -  + ]:         110 :                 if (orig_val == -1) asar_throw_error(0, error_type_block, error_id_invalid_utf8);
     839         [ -  + ]:         110 :                 if (*str != '\'') asar_throw_error(2, error_type_block, error_id_invalid_character);
     840                 :         110 :                 int64_t rval=thetable.get_val(orig_val);
     841         [ -  + ]:         110 :                 if (rval == -1)
     842                 :             :                 {
     843                 :             :                         // RPG Hacker: Should be fine to not check return value of codepoint_to_utf8() here, because
     844                 :             :                         // our error cases above already made sure that orig_val contains valid data at this point.
     845                 :           0 :                         string u8_str;
     846                 :           0 :                         codepoint_to_utf8(&u8_str, orig_val);
     847                 :           0 :                         asar_throw_error(2, error_type_block, error_id_undefined_char, u8_str.data());
     848                 :           0 :                 }
     849                 :         110 :                 str++;
     850                 :         110 :                 return rval;
     851                 :             :         }
     852         [ +  + ]:        5373 :         if (is_digit(*str))
     853                 :             :         {
     854                 :             :                 const char* end = str;
     855   [ +  +  +  + ]:       12517 :                 while (is_digit(*end) || *end == '.') end++;
     856                 :        5368 :                 string number;
     857                 :        5368 :                 number.assign(str, (int)(end - str));
     858                 :        5368 :                 str = end;
     859                 :        5368 :                 return atof(number);
     860                 :        5368 :         }
     861                 :           5 :         asar_throw_error(2, error_type_block, error_id_invalid_number);
     862                 :           0 :         return 0.0;
     863                 :             : }
     864                 :             : 
     865                 :        8525 : inline double sanitize(double val)
     866                 :             : {
     867         [ -  + ]:        8525 :         if (val != val) asar_throw_error(2, error_type_block, error_id_nan);
     868                 :        8525 :         return val;
     869                 :             : }
     870                 :             : 
     871                 :        8534 : static double getnum()
     872                 :             : {
     873         [ +  + ]:        9695 :         while (*str==' ') str++;
     874         [ +  + ]:        8534 :         if(*str == '$') return sanitize(getnumcore());  //optimize for the common case
     875                 :             : #define prefix(sym, func) if (*str == sym) { str+=1; double val=getnum(); return sanitize(func); }
     876                 :             : #define prefix2(sym, sym2, func) if (*str == sym && *(str+1) == sym2) { str+=2; double val=getnum(); return sanitize(func); }
     877                 :        6216 :         prefix('-', -val);
     878                 :        6198 :         prefix('~', ~(int)val);
     879   [ +  +  +  + ]:        6196 :         prefix2('<', ':', (int)val>>16);
     880                 :        6194 :         prefix('+', val);
     881                 :             : #undef prefix
     882                 :             : #undef prefix2
     883                 :        6194 :         return sanitize(getnumcore());
     884                 :             : }
     885                 :             : 
     886                 :        4818 : int64_t getnum(const char* instr)
     887                 :             : {
     888                 :        4818 :         double num = math(instr);
     889         [ +  - ]:        4802 :         if(num < (double)INT64_MIN) {
     890                 :             :                 return INT64_MIN;
     891         [ +  - ]:        4802 :         } else if(num > (double)INT64_MAX) {
     892                 :             :                 return INT64_MAX;
     893                 :             :         }
     894                 :        4802 :         return (int64_t)num;
     895                 :             : }
     896                 :             : 
     897                 :             : // RPG Hacker: Same function as above, but doesn't truncate our number via int conversion
     898                 :         984 : double getnumdouble(const char * instr)
     899                 :             : {
     900                 :         984 :         return math(instr);
     901                 :             : }
     902                 :             : 
     903                 :             : 
     904                 :           1 : static double oper_wrapped_throw(asar_error_id errid)
     905                 :             : {
     906                 :           1 :         asar_throw_error(2, error_type_block, errid);
     907                 :           0 :         return 0.0;
     908                 :             : }
     909                 :             : 
     910                 :        8524 : static double eval(int depth)
     911                 :             : {
     912                 :        8524 :         const char* posneglabel = str;
     913                 :        8524 :         string posnegname = posneglabelname(&posneglabel, false);
     914                 :             : 
     915         [ +  + ]:        8524 :         if (posnegname.length() > 0)
     916                 :             :         {
     917   [ +  +  +  - ]:          32 :                 if (*posneglabel != '\0' && *posneglabel != ')') goto notposneglabel;
     918                 :             : 
     919                 :          14 :                 str = posneglabel;
     920                 :             : 
     921                 :          14 :                 foundlabel=true;
     922         [ +  + ]:          14 :                 if (*(posneglabel-1) == '+') forwardlabel=true;
     923                 :          14 :                 snes_label label_data = labelval(posnegname);
     924                 :          14 :                 foundlabel_static &= label_data.is_static;
     925                 :          14 :                 return label_data.pos & 0xFFFFFF;
     926                 :             :         }
     927                 :        8492 : notposneglabel:
     928                 :       17020 :         recurseblock rec;
     929                 :        8510 :         double left=getnum();
     930                 :             :         double right;
     931         [ +  + ]:        9662 :         while (*str==' ') str++;
     932   [ +  +  +  +  :       10910 :         while (*str && *str != ')' && *str != ','&& *str != ']')
                   +  + ]
     933                 :             :         {
     934         [ -  + ]:        2435 :                 while (*str==' ') str++;
     935                 :             : #define oper(name, thisdepth, contents)      \
     936                 :             :                         if (!strncmp(str, name, strlen(name)))       \
     937                 :             :                         {                                            \
     938                 :             :                                 if (depth<=thisdepth)                \
     939                 :             :                                 {                                    \
     940                 :             :                                         str+=strlen(name);           \
     941                 :             :                                         right=eval(thisdepth+1);     \
     942                 :             :                                 }                                    \
     943                 :             :                                 else return left;                    \
     944                 :             :                                 left=contents;                       \
     945                 :             :                                 continue;                            \
     946                 :             :                         }
     947                 :        2435 :                 oper("**", 6, pow((double)left, (double)right));
     948                 :        2429 :                 oper("*", 5, left*right);
     949   [ +  +  +  -  :        2414 :                 oper("/", 5, right != 0.0 ? left / right : oper_wrapped_throw(error_id_division_by_zero));
          +  -  +  +  -  
                      + ]
     950   [ -  +  -  -  :        2410 :                 oper("%", 5, right != 0.0 ? fmod((double)left, (double)right) : oper_wrapped_throw(error_id_modulo_by_zero));
          -  -  -  -  -  
                      - ]
     951                 :        2410 :                 oper("+", 4, left+right);
     952                 :        1439 :                 oper("-", 4, left-right);
     953   [ -  +  -  -  :        1176 :                 oper("<<", 3, right >= 0.0 ? (int64_t)left<<(uint64_t)right : oper_wrapped_throw(error_id_negative_shift));
          -  -  -  -  -  
                      - ]
     954   [ -  +  -  -  :        1176 :                 oper(">>", 3, right >= 0.0 ? (int64_t)left>>(uint64_t)right : oper_wrapped_throw(error_id_negative_shift));
          -  -  -  -  -  
                      - ]
     955                 :             : 
     956                 :             :                 //these two needed checked early to avoid bitwise from eating a operator
     957   [ +  +  +  +  :        1179 :                 oper("&&", 0, (int64_t)left&&(int64_t)right);
          +  -  +  -  +  
                      + ]
     958   [ +  +  +  +  :        1164 :                 oper("||", 0, (int64_t)left||(int64_t)right);
          +  -  +  -  -  
                      + ]
     959                 :        1158 :                 oper("&", 2, (int64_t)left&(int64_t)right);
     960                 :        1158 :                 oper("|", 2, (int64_t)left|(int64_t)right);
     961                 :        1158 :                 oper("^", 2, (int64_t)left^(int64_t)right);
     962                 :             : 
     963   [ +  +  +  -  :        1158 :                 oper(">=", 1, left>=right);
             +  -  -  + ]
     964   [ -  +  -  -  :        1155 :                 oper("<=", 1, left<=right);
             -  -  -  - ]
     965   [ +  +  +  -  :        1170 :                 oper(">", 1, left>right);
             +  -  +  + ]
     966   [ +  +  +  -  :        1185 :                 oper("<", 1, left<right);
             +  -  +  + ]
     967   [ +  +  +  -  :         309 :                 oper("==", 1, left==right);
             +  -  +  + ]
     968   [ +  +  +  -  :           9 :                 oper("!=", 1, left!=right);
             +  -  -  + ]
     969                 :           6 :                 asar_throw_error(2, error_type_block, error_id_unknown_operator);
     970                 :             : #undef oper
     971                 :             :         }
     972                 :             :         return left;
     973                 :        8524 : }
     974                 :             : 
     975                 :             : //static autoptr<char*> freeme;
     976                 :        5802 : double math(const char * s)
     977                 :             : {
     978                 :             :         //free(freeme);
     979                 :             :         //freeme=NULL;
     980                 :        5802 :         foundlabel=false;
     981                 :        5802 :         foundlabel_static=true;
     982                 :        5802 :         forwardlabel=false;
     983                 :        5802 :         str = s;
     984                 :        5802 :         double rval =  eval(0);
     985         [ -  + ]:        5786 :         if (*str)
     986                 :             :         {
     987         [ #  # ]:           0 :                 if (*str == ',') asar_throw_error(2, error_type_block, error_id_invalid_input);
     988                 :           0 :                 else asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
     989                 :             :         }
     990                 :        5786 :         return rval;
     991                 :             : }
     992                 :             : 
     993                 :         335 : void initmathcore()
     994                 :             : {
     995                 :         335 :         functions.reset();
     996                 :         335 :         builtin_functions.each([](const char* key, double (*val)()) {
     997                 :       21775 :                 functions[key] = val;
     998                 :       21775 :         });
     999                 :         335 :         user_functions.reset();
    1000                 :         335 : }
    1001                 :             : 
    1002                 :         330 : void deinitmathcore()
    1003                 :             : {
    1004                 :             :         //not needed
    1005                 :         330 : }
        

Generated by: LCOV version 2.0-1