LCOV - code coverage report
Current view: top level - asar - macro.cpp (source / functions) Coverage Total Hit
Test: asar.info Lines: 89.8 % 176 158
Test Date: 2024-01-15 16:23:49 Functions: 100.0 % 5 5
Branches: 60.1 % 228 137

             Branch data     Line data    Source code
       1                 :             : #include "libstr.h"
       2                 :             : #include "asar.h"
       3                 :             : #include "autoarray.h"
       4                 :             : #include "assocarr.h"
       5                 :             : #include "errors.h"
       6                 :             : #include "assembleblock.h"
       7                 :             : #include "macro.h"
       8                 :             : #include "asar_math.h"
       9                 :             : #include "warnings.h"
      10                 :             : 
      11                 :             : assocarr<macrodata*> macros;
      12                 :             : static string thisname;
      13                 :             : static macrodata * thisone;
      14                 :             : static int numlines;
      15                 :             : 
      16                 :             : int calledmacros;
      17                 :             : int reallycalledmacros;
      18                 :             : int macrorecursion;
      19                 :             : bool inmacro;
      20                 :             : int numvarargs;
      21                 :             : 
      22                 :             : macrodata* current_macro;
      23                 :             : const char* const* current_macro_args;
      24                 :             : int current_macro_numargs;
      25                 :             : 
      26                 :          26 : void startmacro(const char * line_)
      27                 :             : {
      28                 :          26 :         thisone= nullptr;
      29         [ -  + ]:          26 :         if (!confirmqpar(line_)) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
      30                 :          26 :         string line=line_;
      31                 :          26 :         clean_and_trim(line);
      32                 :          26 :         char * startpar=strqchr(line.data(), '(');
      33   [ -  +  -  - ]:          26 :         if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
      34                 :          26 :         *startpar=0;
      35                 :          26 :         startpar++;
      36   [ +  -  -  +  :          26 :         if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_invalid_macro_name);
                   -  - ]
      37                 :             :         thisname=line;
      38                 :          26 :         char * endpar=strqrchr(startpar, ')');
      39                 :             :         //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nullptrs
      40   [ -  +  -  - ]:          26 :         if (endpar[1]) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
      41                 :          26 :         *endpar=0;
      42         [ +  + ]:         136 :         for (int i=0;startpar[i];i++)
      43                 :             :         {
      44                 :             :                 char c=startpar[i];
      45   [ +  +  +  +  :         110 :                 if (!is_alnum(c) && c!='_' && c!=','&& c!='.') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
             -  +  -  - ]
      46   [ +  +  -  +  :         110 :                 if (c==',' && is_digit(startpar[i+1])) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
                   -  - ]
      47                 :             :         }
      48   [ +  -  +  -  :          26 :         if (*startpar==',' || is_digit(*startpar) || strstr(startpar, ",,") || endpar[-1]==',') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
          +  -  -  +  -  
                      - ]
      49   [ -  +  -  - ]:          26 :         if (macros.exists(thisname)) asar_throw_error(0, error_type_block, error_id_macro_redefined, thisname.data());
      50                 :          26 :         thisone=(macrodata*)malloc(sizeof(macrodata));
      51                 :             :         new(thisone) macrodata;
      52         [ +  + ]:          26 :         if (*startpar)
      53                 :             :         {
      54                 :          16 :                 thisone->arguments=(const char* const*)qpsplit(duplicate_string(startpar), ",", &thisone->numargs);
      55                 :             :         }
      56                 :             :         else
      57                 :             :         {
      58                 :          10 :                 const char ** noargs=(const char**)malloc(sizeof(const char**));
      59                 :          10 :                 *noargs=nullptr;
      60                 :          10 :                 thisone->arguments=noargs;
      61                 :          10 :                 thisone->numargs=0;
      62                 :             :         }
      63                 :          26 :         thisone->variadic = false;
      64                 :          26 :         thisone->fname= duplicate_string(thisfilename);
      65                 :          26 :         thisone->startline=thisline;
      66         [ +  + ]:          48 :         for (int i=0;thisone->arguments[i];i++)
      67                 :             :         {
      68   [ +  +  +  + ]:          23 :                 if(!strcmp(thisone->arguments[i], "...") && !thisone->arguments[i+1]) thisone->variadic = true;
      69   [ +  +  -  + ]:          12 :                 else if(!strcmp(thisone->arguments[i], "...")) asar_throw_error(0, error_type_block, error_id_vararg_must_be_last);
      70   [ -  +  -  - ]:          11 :                 else if(strchr(thisone->arguments[i], '.')) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
      71   [ +  -  -  +  :          11 :                 else if (!confirmname(thisone->arguments[i])) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
                   -  - ]
      72         [ +  + ]:          29 :                 for (int j=i+1;thisone->arguments[j];j++)
      73                 :             :                 {
      74   [ -  +  -  - ]:           7 :                         if (!strcmp(thisone->arguments[i], thisone->arguments[j])) asar_throw_error(0, error_type_block, error_id_macro_param_redefined, thisone->arguments[i]);
      75                 :             :                 }
      76                 :             :         }
      77                 :          25 :         numlines=0;
      78                 :          26 : }
      79                 :             : 
      80                 :          64 : void tomacro(const char * line)
      81                 :             : {
      82         [ +  - ]:          64 :         if (!thisone) return;
      83                 :          64 :         thisone->lines[numlines++]=line;
      84                 :             : }
      85                 :             : 
      86                 :          26 : void endmacro(bool insert)
      87                 :             : {
      88         [ +  - ]:          26 :         if (!thisone) return;
      89                 :          26 :         thisone->numlines=numlines;
      90         [ +  - ]:          26 :         if (insert) macros.create(thisname) = thisone;
      91                 :           0 :         else delete thisone;
      92                 :             : }
      93                 :             : 
      94                 :             : 
      95                 :         108 : void callmacro(const char * data)
      96                 :             : {
      97                 :         108 :         int prev_numvarargs = numvarargs;
      98                 :             :         macrodata * thismacro;
      99         [ -  + ]:         108 :         if (!confirmqpar(data)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
     100                 :         108 :         string line=data;
     101                 :         108 :         clean_and_trim(line);
     102                 :         108 :         char * startpar=strqchr(line.data(), '(');
     103   [ -  +  -  - ]:         108 :         if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
     104                 :         108 :         *startpar=0;
     105                 :         108 :         startpar++;
     106   [ +  -  -  +  :         108 :         if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
                   -  - ]
     107   [ -  +  -  - ]:         108 :         if (!macros.exists(line)) asar_throw_error(0, error_type_block, error_id_macro_not_found, line.data());
     108                 :         108 :         thismacro = macros.find(line);
     109                 :         108 :         char * endpar=strqrchr(startpar, ')');
     110   [ -  +  -  - ]:         108 :         if (endpar[1]) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
     111                 :         108 :         *endpar=0;
     112                 :             :         autoptr<const char * const*> args;
     113                 :         108 :         int numargs=0;
     114   [ +  +  +  - ]:         108 :         if (*startpar) args=(const char* const*)qpsplit(startpar, ",", &numargs);
     115   [ +  +  -  +  :         108 :         if (numargs != thismacro->numargs && !thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_num_params);
                   -  - ]
     116                 :             :         // RPG Hacker: -1, because the ... is also counted as an argument, yet we want it to be entirely optional.
     117   [ +  +  +  -  :         108 :         if (numargs < thismacro->numargs - 1 && thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_min_params);
                   -  + ]
     118                 :             : 
     119                 :         102 :         macrorecursion++;
     120                 :         102 :         inmacro=true;
     121                 :         102 :         int old_calledmacros = calledmacros;
     122                 :         102 :         calledmacros = reallycalledmacros++;
     123                 :         102 :         int startif=numif;
     124                 :             : 
     125                 :             :         // RPG Hacker: -1 to take the ... into account, which is also being counted.
     126         [ +  + ]:         102 :         if(thismacro->variadic) numvarargs = numargs-(thismacro->numargs-1);
     127                 :          63 :         else numvarargs = -1;
     128                 :             : 
     129                 :         102 :         autoarray<int>* oldmacroposlabels = macroposlabels;
     130                 :         102 :         autoarray<int>* oldmacroneglabels = macroneglabels;
     131                 :         102 :         autoarray<string>* oldmacrosublabels = macrosublabels;
     132                 :             : 
     133                 :         102 :         autoarray<int> newmacroposlabels;
     134                 :         102 :         autoarray<int> newmacroneglabels;
     135                 :         102 :         autoarray<string> newmacrosublabels;
     136                 :             : 
     137                 :         102 :         macroposlabels = &newmacroposlabels;
     138                 :         102 :         macroneglabels = &newmacroneglabels;
     139                 :         102 :         macrosublabels = &newmacrosublabels;
     140                 :             : 
     141                 :         102 :         macrodata* old_macro = current_macro;
     142                 :         102 :         const char* const* old_macro_args = current_macro_args;
     143                 :         102 :         int old_numargs = current_macro_numargs;
     144                 :         102 :         current_macro = thismacro;
     145                 :         102 :         current_macro_args = args;
     146                 :         102 :         current_macro_numargs = numargs;
     147                 :             : 
     148         [ +  + ]:         618 :         for (int i=0;i<thismacro->numlines;i++)
     149                 :             :         {
     150                 :             :                 try
     151                 :             :                 {
     152                 :         516 :                         thisfilename= thismacro->fname;
     153                 :         516 :                         thisline= thismacro->startline+i+1;
     154                 :         516 :                         thisblock= nullptr;
     155                 :         516 :                         string connectedline;
     156                 :         516 :                         int skiplines = getconnectedlines<autoarray<string> >(thismacro->lines, i, connectedline);
     157                 :             :                         //string out = replace_macro_args(connectedline); // done in assembleline
     158                 :         516 :                         int prevnumif = numif;
     159                 :         516 :                         assembleline(thismacro->fname, thismacro->startline+i, connectedline);
     160                 :         516 :                         i += skiplines;
     161   [ +  +  -  +  :         684 :                         if ((numif != prevnumif || single_line_for_tracker == 3) && (whilestatus[numif].iswhile || whilestatus[numif].is_for) && whilestatus[numif].cond)
          +  +  +  -  +  
                      + ]
     162                 :          66 :                                 i = whilestatus[numif].startline - thismacro->startline - 1;
     163                 :         516 :                 }
     164                 :           0 :                 catch(errline&){}
     165                 :             :         }
     166                 :             : 
     167                 :         102 :         current_macro = old_macro;
     168                 :         102 :         current_macro_args = old_macro_args;
     169                 :         102 :         current_macro_numargs = old_numargs;
     170                 :             : 
     171                 :         102 :         macroposlabels = oldmacroposlabels;
     172                 :         102 :         macroneglabels = oldmacroneglabels;
     173                 :         102 :         macrosublabels = oldmacrosublabels;
     174                 :             : 
     175                 :         102 :         macrorecursion--;
     176                 :         102 :         inmacro = macrorecursion;
     177                 :         102 :         numvarargs = prev_numvarargs;
     178                 :         102 :         calledmacros = old_calledmacros;
     179         [ -  + ]:         102 :         if (repeatnext!=1)
     180                 :             :         {
     181                 :           0 :                 thisblock= nullptr;
     182                 :           0 :                 repeatnext=1;
     183                 :           0 :                 asar_throw_error(0, error_type_block, error_id_rep_at_macro_end);
     184                 :             :         }
     185         [ -  + ]:         102 :         if (numif!=startif)
     186                 :             :         {
     187                 :           0 :                 thisblock= nullptr;
     188                 :           0 :                 numif=startif;
     189                 :           0 :                 numtrue=startif;
     190                 :           0 :                 asar_throw_error(0, error_type_block, error_id_unclosed_if);
     191                 :             :         }
     192                 :         114 : }
     193                 :             : 
     194                 :         516 : string replace_macro_args(const char* line) {
     195                 :         516 :         string out;
     196         [ +  + ]:        6690 :         for (const char * in=line;*in;)
     197                 :             :         {
     198   [ +  +  -  +  :        6195 :                 if (*in=='<' && in[1]=='<' && in[2] != ':')
                   -  - ]
     199                 :             :                 {
     200                 :           0 :                         out+="<<";
     201                 :           0 :                         in+=2;
     202                 :             :                 }
     203         [ +  + ]:        6195 :                 else if (*in=='<')
     204                 :             :                 {
     205                 :         183 :                         const char * end=in+1;
     206                 :             :                         // RPG Hacker: Added checking for space here, because this code would consider
     207                 :             :                         // if a < b && a > c
     208                 :             :                         // a macro arg expansion. In practice, this is still a sloppy solution and is
     209                 :             :                         // likely to fail in some edge case I can't think of right now. Should parse
     210                 :             :                         // this in a much more robust way at some point...
     211         [ +  + ]:         183 :                         if (*end==' ')
     212                 :             :                         {
     213                 :          42 :                                 out += *(in++);
     214                 :          42 :                                 continue;
     215                 :             :                         }
     216                 :             : 
     217   [ +  -  +  +  :        1104 :                         while (*end && *end!='>'&& *end!='<' && *(end+1)!=':') end++; //allow for conditionals and <:
             +  -  +  - ]
     218         [ -  + ]:         141 :                         if (*end!='>')
     219                 :             :                         {
     220                 :           0 :                                 out+=*(in++);
     221                 :           0 :                                 continue;
     222                 :             :                         }
     223                 :             :                                         
     224                 :             :                         bool proper_variadic = false;
     225   [ +  +  +  -  :         141 :                         if (in[1] == '.' && in[2] == '.' && in[3] == '.' && in[4] == '[')
             +  -  +  - ]
     226                 :             :                         {
     227         [ +  + ]:          78 :                                 if (end[-1] != ']')
     228                 :           3 :                                         asar_throw_error(0, error_type_block, error_id_unclosed_vararg);
     229                 :             :                                                         
     230                 :             :                                 proper_variadic = true;
     231                 :          75 :                                 in += 4;
     232                 :          75 :                                 end--;
     233                 :             :                         }
     234                 :             : 
     235   [ -  +  -  - ]:         138 :                         if(!inmacro) asar_throw_error(0, error_type_block, error_id_macro_param_outside_macro);
     236                 :             :                         //*end=0;
     237                 :         138 :                         in++;
     238                 :         138 :                         string param;
     239                 :         138 :                         string temp(in, end-in);
     240                 :         138 :                         resolvedefines(param, temp);
     241                 :             :                         in = param.data();
     242                 :         138 :                         bool valid_named_param = confirmname(in);
     243   [ +  +  +  +  :         138 :                         if (!valid_named_param && !current_macro->variadic) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
                   -  + ]
     244                 :             :                         bool found=false;
     245   [ +  +  +  + ]:         162 :                         for (int j=0;current_macro->arguments[j]&&!proper_variadic;j++)
     246                 :             :                         {
     247         [ +  + ]:          78 :                                 if (!strcmp(in, current_macro->arguments[j]))
     248                 :             :                                 {
     249                 :             :                                         found=true;
     250         [ +  + ]:          51 :                                         if (current_macro_args[j][0]=='"')
     251                 :             :                                         {
     252                 :          36 :                                                 string s=current_macro_args[j];
     253                 :          36 :                                                 out+=safedequote(s.temp_raw());
     254                 :          36 :                                         }
     255                 :          15 :                                         else out+=current_macro_args[j];
     256                 :             :                                         break;
     257                 :             :                                 }
     258                 :             :                         }
     259                 :             :                         if (!found)
     260                 :             :                         {
     261                 :             :                                 snes_label ret;
     262   [ +  +  +  + ]:          84 :                                 if(valid_named_param && !current_macro->variadic)
     263                 :             :                                 {
     264   [ +  -  -  + ]:           3 :                                         if (proper_variadic) asar_throw_error(0, error_type_block, error_id_invalid_vararg, in);
     265                 :           0 :                                         else asar_throw_error(0, error_type_block, error_id_macro_param_not_found, in);
     266                 :             :                                 }
     267   [ +  -  +  +  :          84 :                                 if(current_macro->variadic && valid_named_param && !labelval(in, &ret, false))
          +  -  +  +  +  
                      + ]
     268                 :             :                                 {
     269   [ +  -  -  + ]:           3 :                                         if (proper_variadic) asar_throw_error(0, error_type_block, error_id_invalid_vararg, in);
     270                 :           0 :                                         else asar_throw_error(0, error_type_block, error_id_macro_param_not_found, in);
     271                 :             :                                 }
     272   [ +  +  +  - ]:          78 :                                 if(!proper_variadic) asar_throw_warning(0, warning_id_feature_deprecated, "'<math>' syntax for variadic macro parameters", "Use '<...[math]>' instead.");
     273                 :          78 :                                 int arg_num = getnum(in);
     274                 :             : 
     275   [ -  +  -  - ]:          78 :                                 if(forwardlabel) asar_throw_error(0, error_type_block, error_id_label_forward);
     276                 :             : 
     277         [ +  + ]:          78 :                                 if(numif<=numtrue){
     278   [ +  +  -  + ]:          69 :                                         if (arg_num < 0) asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds);
     279   [ +  +  -  + ]:          66 :                                         if (arg_num > current_macro_numargs-current_macro->numargs) asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds);
     280         [ -  + ]:          60 :                                         if (current_macro_args[arg_num+current_macro->numargs-1][0]=='"')
     281                 :             :                                         {
     282                 :           0 :                                                 string s=current_macro_args[arg_num+current_macro->numargs-1];
     283                 :           0 :                                                 out+=safedequote(s.temp_raw());
     284                 :           0 :                                         }
     285                 :          60 :                                         else out+=current_macro_args[arg_num+current_macro->numargs-1];
     286                 :             :                                 }
     287                 :             :                         }
     288                 :         120 :                         in=end+1;
     289         [ +  + ]:         120 :                         if (proper_variadic) in++;
     290                 :         156 :                 }
     291                 :        6012 :                 else out+=*(in++);
     292                 :             :         }
     293                 :         495 :         return out;
     294                 :          21 : }
        

Generated by: LCOV version 2.0-1