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