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 : 70 : void startmacro(const char * line_)
23 : : {
24 : 70 : thisone= nullptr;
25 [ - + ]: 70 : if (!confirmqpar(line_)) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
26 : 70 : string line=line_;
27 : 70 : line.qnormalize();
28 : : char * startpar=(char *)strchr(line.data(), '(');
29 [ - + - - ]: 70 : if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
30 : 70 : *startpar=0;
31 : 140 : startpar++;
32 [ + - - + : 70 : if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_invalid_macro_name);
- - ]
33 : : defining_macro_name=line;
34 : 70 : 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 [ - + - - ]: 70 : if (*endpar != ')') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
37 : 70 : *endpar=0;
38 [ + + ]: 459 : for (int i=0;startpar[i];i++)
39 : : {
40 : : char c=startpar[i];
41 [ + + + + : 389 : if (!is_ualnum(c)&& c!=','&& c!='.'&& c!=' ') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
- + - - ]
42 [ + + - + : 389 : if (c==',' && is_digit(startpar[i+1])) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
- - ]
43 : : }
44 [ + - + - : 70 : if (*startpar==',' || is_digit(*startpar) || strstr(startpar, ",,") || endpar[-1]==',') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
+ - - + -
- ]
45 [ - + - - ]: 70 : if (macros.exists(defining_macro_name)) asar_throw_error(0, error_type_block, error_id_macro_redefined, defining_macro_name.data());
46 : 70 : thisone=(macrodata*)malloc(sizeof(macrodata));
47 : : new(thisone) macrodata;
48 [ + + ]: 70 : if (*startpar)
49 : : {
50 : 47 : char **arguments = split(duplicate_string(startpar), ',', &thisone->numargs);
51 : 47 : thisone->arguments_buffer = arguments[0];
52 [ + + ]: 117 : for (int i=0;arguments[i];i++)
53 : : {
54 : 70 : arguments[i] = strip_whitespace(arguments[i]);
55 : : }
56 : 47 : thisone->arguments=(const char* const*)arguments;
57 : : }
58 : : else
59 : : {
60 : 23 : const char ** noargs=(const char**)malloc(sizeof(const char**));
61 : 23 : *noargs=nullptr;
62 : 23 : thisone->arguments=noargs;
63 : 23 : thisone->arguments_buffer = nullptr;
64 : 23 : thisone->numargs=0;
65 : : }
66 : 70 : thisone->variadic = false;
67 : 70 : thisone->fname= duplicate_string(get_current_file_name());
68 : 70 : thisone->startline=get_current_line();
69 : 70 : thisone->parent_macro=current_macro;
70 : 70 : thisone->parent_macro_num_varargs=0;
71 : : // RPG Hacker: -1 to take the ... into account, which is also being counted.
72 [ + + ]: 70 : if (thisone->parent_macro != nullptr) thisone->parent_macro_num_varargs = current_macro_numargs-(current_macro->numargs-1);
73 [ + + ]: 138 : for (int i=0;thisone->arguments[i];i++)
74 : : {
75 [ + + + + ]: 69 : if(!strcmp(thisone->arguments[i], "...") && !thisone->arguments[i+1]) thisone->variadic = true;
76 [ + + - + ]: 44 : else if(!strcmp(thisone->arguments[i], "...")) asar_throw_error(0, error_type_block, error_id_vararg_must_be_last);
77 [ - + - - ]: 43 : else if(strchr(thisone->arguments[i], '.')) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
78 [ + - - + : 43 : else if (!confirmname(thisone->arguments[i])) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
- - ]
79 [ + + ]: 93 : for (int j=i+1;thisone->arguments[j];j++)
80 : : {
81 [ - + - - ]: 25 : 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 : 69 : numlines=0;
85 : 70 : }
86 : :
87 : 411 : void tomacro(const char * line)
88 : : {
89 [ + - ]: 411 : if (!thisone) return;
90 : 411 : thisone->lines[numlines++]=line;
91 : : }
92 : :
93 : 70 : void endmacro(bool insert)
94 : : {
95 [ + - ]: 70 : if (!thisone) return;
96 : 70 : thisone->numlines=numlines;
97 [ + + ]: 70 : if (insert) macros.create(defining_macro_name) = thisone;
98 : : else
99 : : {
100 : 1 : freemacro(thisone);
101 : 1 : thisone=nullptr;
102 : : }
103 : : }
104 : :
105 : : #define cfree(x) free((void*)x)
106 : 70 : void freemacro(macrodata* & macro)
107 : : {
108 : 70 : macro->lines.~autoarray();
109 : 70 : cfree(macro->fname);
110 : 70 : cfree(macro->arguments_buffer);
111 : 70 : cfree(macro->arguments);
112 : 70 : cfree(macro);
113 : 70 : }
114 : : #undef cfree
115 : :
116 : :
117 : 261 : void callmacro(const char * data)
118 : : {
119 : 261 : int prev_numvarargs = numvarargs;
120 : : macrodata * thismacro;
121 [ - + ]: 261 : if (!confirmqpar(data)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
122 : 261 : string line=data;
123 : 261 : line.qnormalize();
124 : : char * startpar=(char *)strchr(line.data(), '(');
125 [ - + - - ]: 261 : if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
126 : 261 : *startpar=0;
127 : 261 : startpar++;
128 [ + - - + : 261 : if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage);
- - ]
129 [ - + - - ]: 261 : if (!macros.exists(line)) asar_throw_error(0, error_type_block, error_id_macro_not_found, line.data());
130 : 261 : thismacro = macros.find(line);
131 : 261 : 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 [ - + - - ]: 261 : if (*endpar != ')') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration);
134 : 261 : *endpar=0;
135 : : autoptr<const char * const*> args;
136 : 261 : int numargs=0;
137 [ + + + - ]: 261 : if (*startpar) args=(const char* const*)qpsplit(startpar, ',', &numargs);
138 [ + + - + : 261 : 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 [ + + + - : 261 : if (numargs < thismacro->numargs - 1 && thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_min_params);
- + ]
141 : :
142 : 255 : macrorecursion++;
143 : 255 : inmacro=true;
144 : 255 : int old_calledmacros = calledmacros;
145 : 255 : calledmacros = reallycalledmacros++;
146 : 255 : int startif=numif;
147 : :
148 [ + + ]: 696 : for (int i = 0; i < numargs; ++i)
149 : : {
150 : : // RPG Hacker: These casts make me feel very nasty.
151 [ + - ]: 441 : (*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 [ + + ]: 255 : if(thismacro->variadic) numvarargs = numargs-(thismacro->numargs-1);
156 : 165 : else numvarargs = -1;
157 : :
158 : 255 : autoarray<int>* oldmacroposlabels = macroposlabels;
159 : 255 : autoarray<int>* oldmacroneglabels = macroneglabels;
160 : 255 : autoarray<string>* oldmacrosublabels = macrosublabels;
161 : :
162 : 255 : autoarray<int> newmacroposlabels;
163 : 255 : autoarray<int> newmacroneglabels;
164 : 255 : autoarray<string> newmacrosublabels;
165 : :
166 : 255 : macroposlabels = &newmacroposlabels;
167 : 255 : macroneglabels = &newmacroneglabels;
168 : 255 : macrosublabels = &newmacrosublabels;
169 : :
170 : 255 : macrodata* old_macro = current_macro;
171 : 255 : const char* const* old_macro_args = current_macro_args;
172 : 255 : int old_numargs = current_macro_numargs;
173 : 255 : current_macro = thismacro;
174 : 255 : current_macro_args = args;
175 : 255 : current_macro_numargs = numargs;
176 : :
177 : 255 : callstack_push cs_push(callstack_entry_type::MACRO_CALL, data);
178 : :
179 : : {
180 : 255 : callstack_push cs_push(callstack_entry_type::FILE, thismacro->fname);
181 : :
182 [ + + ]: 3765 : for (int i=0;i<thismacro->numlines;i++)
183 : : {
184 : 3510 : bool was_loop_end = do_line_logic(thismacro->lines[i], thismacro->fname, thismacro->startline+i+1);
185 : :
186 [ + + + + ]: 3510 : 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 : 291 : i = whilestatus[numif].startline - thismacro->startline - 2;
190 : : }
191 : 255 : }
192 : :
193 : 255 : macroposlabels = oldmacroposlabels;
194 : 255 : macroneglabels = oldmacroneglabels;
195 : 255 : macrosublabels = oldmacrosublabels;
196 : :
197 : 255 : current_macro = old_macro;
198 : 255 : current_macro_args = old_macro_args;
199 : 255 : current_macro_numargs = old_numargs;
200 : :
201 : 255 : macrorecursion--;
202 : 255 : inmacro = macrorecursion;
203 : 255 : numvarargs = prev_numvarargs;
204 : 255 : calledmacros = old_calledmacros;
205 [ - + ]: 255 : 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 : 267 : }
212 : :
213 : 28 : string generate_macro_arg_string(const char* named_arg, int depth)
214 : : {
215 : 28 : string ret="<";
216 [ + + ]: 58 : for (int i = 0; i < depth;++i)
217 : : {
218 : 30 : ret += '^';
219 : : }
220 : 28 : ret += named_arg;
221 : 28 : ret += ">";
222 : 28 : return ret;
223 : : }
224 : :
225 : 21 : string generate_macro_arg_string(int var_arg, int depth)
226 : : {
227 : 21 : string ret="<";
228 [ + + ]: 30 : for (int i = 0; i < depth;++i)
229 : : {
230 : 9 : ret += '^';
231 : : }
232 : 21 : ret += dec(var_arg);
233 : 21 : ret += ">";
234 : 21 : return ret;
235 : : }
236 : :
237 : 38 : 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 [ + + + + ]: 38 : if (current_depth == 0 && thisone != nullptr)
243 : : {
244 [ + + ]: 15 : for (int j=0;thisone->arguments[j];j++)
245 : : {
246 [ + + ]: 10 : if (!strcmp(named_arg, thisone->arguments[j]))
247 : : {
248 : 1 : string ret=" Did you mean: '";
249 : 1 : ret += generate_macro_arg_string(thisone->arguments[j], 0);
250 : 1 : ret += "'?";
251 : 1 : return ret;
252 : 1 : }
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 [ + + ]: 37 : if (thismacro != nullptr)
261 : : {
262 [ + + ]: 66 : for (int j=0;thismacro->arguments[j];j++)
263 : : {
264 [ + + ]: 46 : if (!strcmp(named_arg, thismacro->arguments[j]))
265 : : {
266 : 9 : string ret=" Did you mean: '";
267 : 9 : ret += generate_macro_arg_string(thismacro->arguments[j], desired_depth+current_depth);
268 : 9 : ret += "'?";
269 : 9 : return ret;
270 : 9 : }
271 : : }
272 : 20 : return generate_macro_hint_string(named_arg, thismacro->parent_macro, desired_depth, current_depth+1);
273 : : }
274 : :
275 : 8 : return "";
276 : : }
277 : :
278 : 21 : string generate_macro_hint_string(int var_arg, const macrodata* thismacro, int desired_depth, int current_depth=0)
279 : : {
280 [ + + ]: 21 : if (thismacro != nullptr)
281 : : {
282 [ + + ]: 15 : if (thismacro->parent_macro_num_varargs > var_arg)
283 : : {
284 : 6 : string ret=" Did you mean: '";
285 : 6 : ret += generate_macro_arg_string(var_arg, desired_depth+current_depth+1);
286 : 6 : ret += "'?";
287 : 6 : return ret;
288 : 6 : }
289 : 9 : return generate_macro_hint_string(var_arg, thismacro->parent_macro, desired_depth, current_depth+1);
290 : : }
291 : :
292 : 6 : return "";
293 : : }
294 : :
295 : 21798 : string replace_macro_args(const char* line) {
296 : 21798 : string out;
297 [ + + ]: 21798 : if(!inmacro)
298 : : {
299 : 18774 : out += line;
300 : : return out;
301 : : }
302 [ + + ]: 43065 : for (const char * in=line;*in;)
303 : : {
304 [ + + + + : 40092 : if (*in=='<' && in[1]=='<' && in[2] != ':')
+ - ]
305 : : {
306 [ + - ]: 9 : if (in[2] == '^')
307 : : {
308 : 9 : out+="<";
309 : 9 : in+=1;
310 : : }
311 : : else
312 : : {
313 : 0 : out+="<<";
314 : 0 : in+=2;
315 : : }
316 : : }
317 [ + + ]: 40083 : else if (*in=='<')
318 : : {
319 : 1125 : 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 [ + + ]: 1125 : if (*end==' ')
326 : : {
327 : 360 : out += *(in++);
328 : 807 : continue;
329 : : }
330 : :
331 [ + + + + : 6651 : while (*end && *end!='>'&& *end!='<' && *(end+1)!=':') end++; //allow for conditionals and <:
+ - + - ]
332 [ + + ]: 765 : if (*end!='>')
333 : : {
334 : 9 : out+=*(in++);
335 : 9 : continue;
336 : : }
337 : :
338 : : int depth = 0;
339 [ + + ]: 849 : for (const char* depth_str = in+1; *depth_str=='^'; depth_str++)
340 : : {
341 : 93 : depth++;
342 : : }
343 : :
344 [ + + ]: 756 : if (depth != in_macro_def)
345 : : {
346 : 81 : string temp(in, end-in+1);
347 : 81 : out+=temp;
348 : 81 : in=end+1;
349 [ + + ]: 81 : if (depth > in_macro_def)
350 : : {
351 [ + + - + ]: 9 : 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 : : continue;
355 : 81 : }
356 : :
357 [ + + - + : 675 : 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 : 675 : in += depth+1;
359 : :
360 : : bool is_variadic_arg = false;
361 [ + + + - : 675 : if (in[0] == '.' && in[1] == '.' && in[2] == '.' && in[3] == '[')
+ - + - ]
362 : : {
363 [ + + ]: 315 : if (end[-1] != ']')
364 : 3 : asar_throw_error(0, error_type_block, error_id_unclosed_vararg);
365 : :
366 : : is_variadic_arg = true;
367 : 312 : in += 4;
368 : 312 : end--;
369 : : }
370 : :
371 : : //if(!inmacro) asar_throw_error(0, error_type_block, error_id_macro_param_outside_macro);
372 [ + + + + : 672 : if(is_variadic_arg && !current_macro->variadic) asar_throw_error(0, error_type_block, error_id_macro_not_varadic, "<...[math]>");
- + ]
373 : : //*end=0;
374 : 669 : string param;
375 : 669 : string temp(in, end-in);
376 : 669 : resolvedefines(param, temp);
377 : : in = param.data();
378 : 669 : bool valid_named_param = confirmname(in);
379 [ + + ]: 669 : if (!is_variadic_arg)
380 : : {
381 [ + + - + ]: 360 : if (!valid_named_param) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name);
382 : : bool found=false;
383 [ + + ]: 462 : for (int j=0;current_macro->arguments[j];j++)
384 : : {
385 [ + + ]: 444 : if (!strcmp(in, current_macro->arguments[j]))
386 : : {
387 : : found=true;
388 : 336 : out+=current_macro_args[j];
389 : : break;
390 : : }
391 : : }
392 : : if (!found)
393 : : {
394 : 36 : 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 : 309 : snes_label ret;
400 [ + + + - : 312 : if(valid_named_param && !labelval(in, &ret, false)) asar_throw_error(0, error_type_block, error_id_invalid_vararg, in);
+ + + + -
+ ]
401 : 306 : int arg_num = getnum(in);
402 : :
403 [ - + - - ]: 306 : if(forwardlabel) asar_throw_error(0, error_type_block, error_id_label_forward);
404 : :
405 [ + + - + ]: 306 : 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 [ + + - + ]: 315 : 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 : 291 : out+=current_macro_args[arg_num+current_macro->numargs-1];
408 : : }
409 : 627 : in=end+1;
410 [ + + ]: 627 : if (is_variadic_arg) in++;
411 : 711 : }
412 : 38958 : else out+=*(in++);
413 : : }
414 : : return out;
415 : 51 : }
|