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 : }
|