LCOV - code coverage report
Current view: top level - asar - macro.cpp (source / functions) Coverage Total Hit
Test: asar build #66 Lines: 97.1 % 245 238
Test Date: 2024-01-16 02:45:19 Functions: 100.0 % 10 10
Branches: 68.9 % 286 197

             Branch data     Line data    Source code
       1                 :             : #include "asar.h"
       2                 :             : #include "assembleblock.h"
       3                 :             : #include "macro.h"
       4                 :             : #include "asar_math.h"
       5                 :             : #include "warnings.h"
       6                 :             : 
       7                 :             : assocarr<macrodata*> macros;
       8                 :             : string defining_macro_name;
       9                 :             : static macrodata * thisone;
      10                 :             : static int numlines;
      11                 :             : 
      12                 :             : int calledmacros;
      13                 :             : int reallycalledmacros;
      14                 :             : int macrorecursion;
      15                 :             : bool inmacro;
      16                 :             : int numvarargs;
      17                 :             : 
      18                 :             : macrodata* current_macro;
      19                 :             : const char* const* current_macro_args;
      20                 :             : int current_macro_numargs;
      21                 :             : 
      22                 :         140 : void startmacro(const char * line_)
      23                 :             : {
      24                 :         140 :         thisone= nullptr;
      25   [ +  +  -  +  :         140 :         if (!confirmqpar(line_)) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
                   -  - ]
      26                 :         140 :         string line=line_;
      27                 :         140 :         line.qnormalize();
      28                 :          70 :         char * startpar=(char *)strchr(line.data(), '(');
      29   [ -  +  -  - ]:         140 :         if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
      30                 :         140 :         *startpar=0;
      31                 :         210 :         startpar++;
      32   [ +  -  -  +  :         140 :         if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_invalid_macro_name);
                   -  - ]
      33                 :          70 :         defining_macro_name=line;
      34                 :         140 :         char * endpar=startpar+strlen(startpar)-1;
      35                 :             :         //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nullptrs
      36   [ -  +  -  - ]:         140 :         if (*endpar != ')') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
      37                 :         140 :         *endpar=0;
      38         [ +  + ]:         918 :         for (int i=0;startpar[i];i++)
      39                 :             :         {
      40                 :         389 :                 char c=startpar[i];
      41   [ +  +  +  +  :         778 :                 if (!is_ualnum(c)&& c!=','&& c!='.'&& c!=' ') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
          +  +  -  +  -  
                +  -  - ]
      42   [ +  +  -  +  :         778 :                 if (c==',' && is_digit(startpar[i+1])) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
             -  +  -  - ]
      43                 :             :         }
      44   [ +  -  +  -  :         140 :         if (*startpar==',' || is_digit(*startpar) || strstr(startpar, ",,") || endpar[-1]==',') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
          +  -  -  +  -  
                +  -  - ]
      45   [ +  +  -  +  :         140 :         if (macros.exists(defining_macro_name)) asar_throw_error(0, error_type_block, error_id_macro_redefined, defining_macro_name.data());
                   -  - ]
      46                 :         140 :         thisone=(macrodata*)malloc(sizeof(macrodata));
      47                 :          70 :         new(thisone) macrodata;
      48         [ +  + ]:         140 :         if (*startpar)
      49                 :             :         {
      50                 :          94 :                 char **arguments = split(duplicate_string(startpar), ',', &thisone->numargs);
      51                 :          94 :                 thisone->arguments_buffer = arguments[0];
      52         [ +  + ]:         234 :                 for (int i=0;arguments[i];i++)
      53                 :             :                 {
      54                 :         140 :                         arguments[i] = strip_whitespace(arguments[i]);
      55                 :             :                 }
      56                 :          94 :                 thisone->arguments=(const char* const*)arguments;
      57                 :             :         }
      58                 :             :         else
      59                 :             :         {
      60                 :          46 :                 const char ** noargs=(const char**)malloc(sizeof(const char**));
      61                 :          46 :                 *noargs=nullptr;
      62                 :          46 :                 thisone->arguments=noargs;
      63                 :          46 :                 thisone->arguments_buffer = nullptr;
      64                 :          46 :                 thisone->numargs=0;
      65                 :             :         }
      66                 :         140 :         thisone->variadic = false;
      67                 :         140 :         thisone->fname= duplicate_string(get_current_file_name());
      68                 :         140 :         thisone->startline=get_current_line();
      69                 :         140 :         thisone->parent_macro=current_macro;
      70                 :         140 :         thisone->parent_macro_num_varargs=0;
      71                 :             :         // RPG Hacker: -1 to take the ... into account, which is also being counted.
      72         [ +  + ]:         140 :         if (thisone->parent_macro != nullptr) thisone->parent_macro_num_varargs = current_macro_numargs-(current_macro->numargs-1);
      73         [ +  + ]:         276 :         for (int i=0;thisone->arguments[i];i++)
      74                 :             :         {
      75   [ +  +  +  + ]:         138 :                 if(!strcmp(thisone->arguments[i], "...") && !thisone->arguments[i+1]) thisone->variadic = true;
      76   [ +  +  -  + ]:          88 :                 else if(!strcmp(thisone->arguments[i], "...")) asar_throw_error(0, error_type_block, error_id_vararg_must_be_last);
      77   [ -  +  -  - ]:          86 :                 else if(strchr(thisone->arguments[i], '.')) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
      78   [ +  -  -  +  :          86 :                 else if (!confirmname(thisone->arguments[i])) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
                   -  - ]
      79         [ +  + ]:         186 :                 for (int j=i+1;thisone->arguments[j];j++)
      80                 :             :                 {
      81   [ -  +  -  - ]:          50 :                         if (!strcmp(thisone->arguments[i], thisone->arguments[j])) asar_throw_error(0, error_type_block, error_id_macro_param_redefined, thisone->arguments[i]);
      82                 :             :                 }
      83                 :             :         }
      84                 :         138 :         numlines=0;
      85                 :         140 : }
      86                 :             : 
      87                 :         822 : void tomacro(const char * line)
      88                 :             : {
      89         [ +  + ]:         822 :         if (!thisone) return;
      90                 :         822 :         thisone->lines[numlines++]=line;
      91                 :             : }
      92                 :             : 
      93                 :         140 : void endmacro(bool insert)
      94                 :             : {
      95         [ +  + ]:         140 :         if (!thisone) return;
      96                 :         140 :         thisone->numlines=numlines;
      97         [ +  + ]:         140 :         if (insert) macros.create(defining_macro_name) = thisone;
      98                 :             :         else
      99                 :             :         {
     100                 :           2 :                 freemacro(thisone);
     101                 :           2 :                 thisone=nullptr;
     102                 :             :         }
     103                 :             : }
     104                 :             : 
     105                 :             : #define cfree(x) free((void*)x)
     106                 :         140 : void freemacro(macrodata* & macro)
     107                 :             : {
     108                 :         140 :         macro->lines.~autoarray();
     109                 :         140 :         cfree(macro->fname);
     110                 :         140 :         cfree(macro->arguments_buffer);
     111                 :         140 :         cfree(macro->arguments);
     112                 :         140 :         cfree(macro);
     113                 :         140 : }
     114                 :             : #undef cfree
     115                 :             : 
     116                 :             : 
     117                 :         522 : void callmacro(const char * data)
     118                 :             : {
     119                 :         522 :         int prev_numvarargs = numvarargs;
     120                 :             :         macrodata * thismacro;
     121   [ +  +  -  +  :         522 :         if (!confirmqpar(data)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
                   -  - ]
     122                 :         522 :         string line=data;
     123                 :         522 :         line.qnormalize();
     124                 :         261 :         char * startpar=(char *)strchr(line.data(), '(');
     125   [ -  +  -  - ]:         522 :         if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
     126                 :         522 :         *startpar=0;
     127                 :         522 :         startpar++;
     128   [ +  -  -  +  :         522 :         if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
                   -  - ]
     129   [ +  +  -  +  :         522 :         if (!macros.exists(line)) asar_throw_error(0, error_type_block, error_id_macro_not_found, line.data());
                   -  - ]
     130                 :         522 :         thismacro = macros.find(line);
     131                 :         522 :         char * endpar=startpar+strlen(startpar)-1;
     132                 :             :         //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nullptrs
     133   [ -  +  -  - ]:         522 :         if (*endpar != ')') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
     134                 :         522 :         *endpar=0;
     135                 :         261 :         autoptr<const char * const*> args;
     136                 :         522 :         int numargs=0;
     137   [ +  +  +  - ]:         522 :         if (*startpar) args=(const char* const*)qpsplit(startpar, ',', &numargs);
     138   [ +  +  -  +  :         522 :         if (numargs != thismacro->numargs && !thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_num_params);
                   -  - ]
     139                 :             :         // RPG Hacker: -1, because the ... is also counted as an argument, yet we want it to be entirely optional.
     140   [ +  +  +  -  :         522 :         if (numargs < thismacro->numargs - 1 && thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_min_params);
                   -  + ]
     141                 :             : 
     142                 :         510 :         macrorecursion++;
     143                 :         510 :         inmacro=true;
     144                 :         510 :         int old_calledmacros = calledmacros;
     145                 :         510 :         calledmacros = reallycalledmacros++;
     146                 :         510 :         int startif=numif;
     147                 :             : 
     148         [ +  + ]:        1392 :         for (int i = 0; i < numargs; ++i)
     149                 :             :         {
     150                 :             :                 // RPG Hacker: These casts make me feel very nasty.
     151         [ +  - ]:         882 :                 (*reinterpret_cast<autoptr<const char**>*>(&args))[i] = safedequote(strip_whitespace((char*)args[i]));
     152                 :             :         }
     153                 :             : 
     154                 :             :         // RPG Hacker: -1 to take the ... into account, which is also being counted.
     155         [ +  + ]:         510 :         if(thismacro->variadic) numvarargs = numargs-(thismacro->numargs-1);
     156                 :         330 :         else numvarargs = -1;
     157                 :             : 
     158                 :         510 :         autoarray<int>* oldmacroposlabels = macroposlabels;
     159                 :         510 :         autoarray<int>* oldmacroneglabels = macroneglabels;
     160                 :         510 :         autoarray<string>* oldmacrosublabels = macrosublabels;
     161                 :             : 
     162                 :         510 :         autoarray<int> newmacroposlabels;
     163                 :         510 :         autoarray<int> newmacroneglabels;
     164                 :         510 :         autoarray<string> newmacrosublabels;
     165                 :             : 
     166                 :         510 :         macroposlabels = &newmacroposlabels;
     167                 :         510 :         macroneglabels = &newmacroneglabels;
     168                 :         510 :         macrosublabels = &newmacrosublabels;
     169                 :             : 
     170                 :         510 :         macrodata* old_macro = current_macro;
     171                 :         510 :         const char* const* old_macro_args = current_macro_args;
     172                 :         510 :         int old_numargs = current_macro_numargs;
     173                 :         510 :         current_macro = thismacro;
     174                 :         510 :         current_macro_args = args;
     175                 :         510 :         current_macro_numargs = numargs;
     176                 :             : 
     177                 :         510 :         callstack_push cs_push(callstack_entry_type::MACRO_CALL, data);
     178                 :             : 
     179                 :             :         {
     180                 :         510 :                 callstack_push cs_push(callstack_entry_type::FILE, thismacro->fname);
     181                 :             : 
     182         [ +  + ]:        7530 :                 for (int i=0;i<thismacro->numlines;i++)
     183                 :             :                 {
     184                 :        7020 :                         bool was_loop_end = do_line_logic(thismacro->lines[i], thismacro->fname, thismacro->startline+i+1);
     185                 :             : 
     186   [ +  +  +  +  :        7020 :                         if (was_loop_end && whilestatus[numif].cond)
             +  +  +  + ]
     187                 :             :                                 // RPG Hacker: -1 to compensate for the i++, and another -1
     188                 :             :                                 // because ->lines doesn't include the macro header.
     189                 :         582 :                                 i = whilestatus[numif].startline - thismacro->startline - 2;
     190                 :             :                 }
     191                 :         510 :         }
     192                 :             : 
     193                 :         510 :         macroposlabels = oldmacroposlabels;
     194                 :         510 :         macroneglabels = oldmacroneglabels;
     195                 :         510 :         macrosublabels = oldmacrosublabels;
     196                 :             : 
     197                 :         510 :         current_macro = old_macro;
     198                 :         510 :         current_macro_args = old_macro_args;
     199                 :         510 :         current_macro_numargs = old_numargs;
     200                 :             : 
     201                 :         510 :         macrorecursion--;
     202                 :         510 :         inmacro = macrorecursion;
     203                 :         510 :         numvarargs = prev_numvarargs;
     204                 :         510 :         calledmacros = old_calledmacros;
     205         [ -  + ]:         510 :         if (numif!=startif)
     206                 :             :         {
     207                 :           0 :                 numif=startif;
     208                 :           0 :                 numtrue=startif;
     209                 :           0 :                 asar_throw_error(0, error_type_block, error_id_unclosed_if);
     210                 :             :         }
     211                 :         534 : }
     212                 :             : 
     213                 :          56 : string generate_macro_arg_string(const char* named_arg, int depth)
     214                 :             : {
     215                 :          56 :         string ret="<";
     216         [ +  + ]:         116 :         for (int i = 0; i < depth;++i)
     217                 :             :         {
     218                 :          60 :                 ret += '^';
     219                 :             :         }
     220                 :          56 :         ret += named_arg;
     221                 :          56 :         ret += ">";
     222                 :          56 :         return ret;
     223                 :           0 : }
     224                 :             : 
     225                 :          42 : string generate_macro_arg_string(int var_arg, int depth)
     226                 :             : {
     227                 :          42 :         string ret="<";
     228         [ +  + ]:          60 :         for (int i = 0; i < depth;++i)
     229                 :             :         {
     230                 :          18 :                 ret += '^';
     231                 :             :         }
     232                 :          42 :         ret += dec(var_arg);
     233                 :          42 :         ret += ">";
     234                 :          42 :         return ret;
     235                 :           0 : }
     236                 :             : 
     237                 :          76 : string generate_macro_hint_string(const char* named_arg, const macrodata* thismacro, int desired_depth, int current_depth=0)
     238                 :             : {
     239                 :             :         // RPG Hacker: This only work when the incorrectly used parameter
     240                 :             :         // is inside the macro that is currently being defined. Not great,
     241                 :             :         // but still better than nothing.
     242   [ +  +  +  + ]:          76 :         if (current_depth == 0 && thisone != nullptr)
     243                 :             :         {
     244         [ +  + ]:          30 :                 for (int j=0;thisone->arguments[j];j++)
     245                 :             :                 {
     246         [ +  + ]:          20 :                         if (!strcmp(named_arg, thisone->arguments[j]))
     247                 :             :                         {
     248                 :           2 :                                 string ret=" Did you mean: '";
     249                 :           2 :                                 ret += generate_macro_arg_string(thisone->arguments[j], 0);
     250                 :           2 :                                 ret += "'?";
     251                 :           2 :                                 return ret;
     252                 :           2 :                         }
     253                 :             :                 }
     254                 :             :         }
     255                 :             : 
     256                 :             :         // RPG Hacker: Technically, we could skip a level here and go straight
     257                 :             :         // to the parent, but maybe at some point we'll want to expand this to
     258                 :             :         // also look for similar args in the current level, so I'll leave it
     259                 :             :         // like this, just in case.
     260         [ +  + ]:          74 :         if (thismacro != nullptr)
     261                 :             :         {
     262         [ +  + ]:         132 :                 for (int j=0;thismacro->arguments[j];j++)
     263                 :             :                 {
     264         [ +  + ]:          92 :                         if (!strcmp(named_arg, thismacro->arguments[j]))
     265                 :             :                         {
     266                 :          18 :                                 string ret=" Did you mean: '";
     267                 :          18 :                                 ret += generate_macro_arg_string(thismacro->arguments[j], desired_depth+current_depth);
     268                 :          18 :                                 ret += "'?";
     269                 :          18 :                                 return ret;
     270                 :          18 :                         }
     271                 :             :                 }
     272                 :          40 :                 return generate_macro_hint_string(named_arg, thismacro->parent_macro, desired_depth, current_depth+1);
     273                 :             :         }
     274                 :             : 
     275                 :          16 :         return "";
     276                 :             : }
     277                 :             : 
     278                 :          42 : string generate_macro_hint_string(int var_arg, const macrodata* thismacro, int desired_depth, int current_depth=0)
     279                 :             : {
     280         [ +  + ]:          42 :         if (thismacro != nullptr)
     281                 :             :         {
     282         [ +  + ]:          30 :                 if (thismacro->parent_macro_num_varargs > var_arg)
     283                 :             :                 {
     284                 :          12 :                         string ret=" Did you mean: '";
     285                 :          12 :                         ret += generate_macro_arg_string(var_arg, desired_depth+current_depth+1);
     286                 :          12 :                         ret += "'?";
     287                 :          12 :                         return ret;
     288                 :          12 :                 }
     289                 :          18 :                 return generate_macro_hint_string(var_arg, thismacro->parent_macro, desired_depth, current_depth+1);
     290                 :             :         }
     291                 :             : 
     292                 :          12 :         return "";
     293                 :             : }
     294                 :             : 
     295                 :       45807 : string replace_macro_args(const char* line) {
     296                 :       45807 :         string out;
     297         [ +  + ]:       45807 :         if(!inmacro)
     298                 :             :         {
     299                 :       39759 :                 out += line;
     300                 :       20985 :                 return out;
     301                 :             :         }
     302         [ +  + ]:       86130 :         for (const char * in=line;*in;)
     303                 :             :         {
     304   [ +  +  +  +  :       80184 :                 if (*in=='<' && in[1]=='<' && in[2] != ':')
                   +  - ]
     305                 :             :                 {
     306         [ +  - ]:          18 :                         if (in[2] == '^')
     307                 :             :                         {
     308                 :          18 :                                 out+="<";
     309                 :          18 :                                 in+=1;
     310                 :             :                         }
     311                 :             :                         else
     312                 :             :                         {
     313                 :           0 :                                 out+="<<";
     314                 :           0 :                                 in+=2;
     315                 :             :                         }
     316                 :             :                 }
     317         [ +  + ]:       80166 :                 else if (*in=='<')
     318                 :             :                 {
     319                 :        2250 :                         const char * end=in+1;
     320                 :             :                         // RPG Hacker: Added checking for space here, because this code would consider
     321                 :             :                         // if a < b && a > c
     322                 :             :                         // a macro arg expansion. In practice, this is still a sloppy solution and is
     323                 :             :                         // likely to fail in some edge case I can't think of right now. Should parse
     324                 :             :                         // this in a much more robust way at some point...
     325         [ +  + ]:        2250 :                         if (*end==' ')
     326                 :             :                         {
     327                 :         720 :                                 out += *(in++);
     328                 :        1254 :                                 continue;
     329                 :             :                         }
     330                 :             : 
     331   [ +  +  +  +  :       13302 :                         while (*end && *end!='>'&& *end!='<' && *(end+1)!=':') end++; //allow for conditionals and <:
             +  -  +  - ]
     332         [ +  + ]:        1530 :                         if (*end!='>')
     333                 :             :                         {
     334                 :          18 :                                 out+=*(in++);
     335                 :          18 :                                 continue;
     336                 :             :                         }
     337                 :             : 
     338                 :         756 :                         int depth = 0;
     339         [ +  + ]:        1698 :                         for (const char* depth_str = in+1; *depth_str=='^'; depth_str++)
     340                 :             :                         {
     341                 :         186 :                                 depth++;
     342                 :             :                         }
     343                 :             : 
     344         [ +  + ]:        1512 :                         if (depth != in_macro_def)
     345                 :             :                         {
     346                 :         162 :                                 string temp(in, end-in+1);
     347                 :         162 :                                 out+=temp;
     348                 :         162 :                                 in=end+1;
     349         [ +  + ]:         162 :                                 if (depth > in_macro_def)
     350                 :             :                                 {
     351   [ +  +  -  + ]:          18 :                                         if (in_macro_def > 0) asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, "macro parameter", "macro parameter", depth, in_macro_def-1);
     352                 :             :                                         //else asar_throw_error(0, error_type_block, error_id_macro_param_outside_macro);
     353                 :             :                                 }
     354                 :          78 :                                 continue;
     355                 :         162 :                         }
     356                 :             : 
     357   [ +  +  -  +  :        1350 :                         if (depth > 0 && !inmacro) asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, "macro parameter", "macro parameter", depth, in_macro_def-1);
                   -  - ]
     358                 :        1350 :                         in += depth+1;
     359                 :             : 
     360                 :         675 :                         bool is_variadic_arg = false;
     361   [ +  +  +  -  :        1350 :                         if (in[0] == '.' && in[1] == '.' && in[2] == '.' && in[3] == '[')
             +  -  +  - ]
     362                 :             :                         {
     363         [ +  + ]:         630 :                                 if (end[-1] != ']')
     364                 :           6 :                                         asar_throw_error(0, error_type_block, error_id_unclosed_vararg);
     365                 :             : 
     366                 :         312 :                                 is_variadic_arg = true;
     367                 :         624 :                                 in += 4;
     368                 :         624 :                                 end--;
     369                 :             :                         }
     370                 :             : 
     371                 :             :                         //if(!inmacro) asar_throw_error(0, error_type_block, error_id_macro_param_outside_macro);
     372   [ +  +  +  +  :        1344 :                         if(is_variadic_arg && !current_macro->variadic) asar_throw_error(0, error_type_block, error_id_macro_not_varadic, "<...[math]>");
                   -  + ]
     373                 :             :                         //*end=0;
     374                 :        1338 :                         string param;
     375                 :        1338 :                         string temp(in, end-in);
     376                 :        1338 :                         resolvedefines(param, temp);
     377                 :         669 :                         in = param.data();
     378                 :        1338 :                         bool valid_named_param = confirmname(in);
     379         [ +  + ]:        1338 :                         if (!is_variadic_arg)
     380                 :             :                         {
     381   [ +  +  -  + ]:         720 :                                 if (!valid_named_param) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
     382                 :         354 :                                 bool found=false;
     383         [ +  + ]:         924 :                                 for (int j=0;current_macro->arguments[j];j++)
     384                 :             :                                 {
     385         [ +  + ]:         888 :                                         if (!strcmp(in, current_macro->arguments[j]))
     386                 :             :                                         {
     387                 :         336 :                                                 found=true;
     388                 :         672 :                                                 out+=current_macro_args[j];
     389                 :         336 :                                                 break;
     390                 :             :                                         }
     391                 :             :                                 }
     392         [ +  + ]:         354 :                                 if (!found)
     393                 :             :                                 {
     394                 :          90 :                                         asar_throw_error(0, error_type_block, error_id_macro_param_not_found, generate_macro_arg_string(in, depth).raw(), generate_macro_hint_string(in, current_macro, depth).raw());
     395                 :             :                                 }
     396                 :             :                         }
     397                 :             :                         else
     398                 :             :                         {
     399                 :         618 :                                 snes_label ret;
     400   [ +  +  +  -  :         621 :                                 if(valid_named_param && !labelval(in, &ret, false)) asar_throw_error(0, error_type_block, error_id_invalid_vararg, in);
          +  +  +  +  +  
          +  +  +  -  +  
                   -  - ]
     401                 :         612 :                                 int arg_num = getnum(in);
     402                 :             : 
     403   [ -  +  -  - ]:         612 :                                 if(forwardlabel) asar_throw_error(0, error_type_block, error_id_label_forward);
     404                 :             : 
     405   [ +  +  +  +  :         615 :                                 if (arg_num < 0) asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds, generate_macro_arg_string(arg_num, depth).raw(), "");
                   -  + ]
     406   [ +  +  +  +  :         642 :                                 if (arg_num > current_macro_numargs-current_macro->numargs) asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds, generate_macro_arg_string(arg_num, depth).raw(), generate_macro_hint_string(arg_num, current_macro, depth).raw());
             +  -  -  + ]
     407                 :         582 :                                 out+=current_macro_args[arg_num+current_macro->numargs-1];
     408                 :             :                         }
     409                 :        1254 :                         in=end+1;
     410         [ +  + ]:        1254 :                         if (is_variadic_arg) in++;
     411                 :        1422 :                 }
     412                 :       77916 :                 else out+=*(in++);
     413                 :             :         }
     414                 :        2973 :         return out;
     415                 :         102 : }
        

Generated by: LCOV version 2.0-1