asar coverage - build #76


src/asar/
File: src/asar/asar_math.cpp
Date: 2024-01-18 11:25:05
Lines:
420/461
91.1%
Functions:
68/92
73.9%
Branches:
352/632
55.7%

Line Branch Exec Source
1 //Don't try using this in your own project, it's got a lot of Asar-specific tweaks. Use mathlib.cpp instead.
2 #include "platform/file-helpers.h"
3 #include "std-includes.h"
4 #include "autoarray.h"
5 #include "assocarr.h"
6 #include "libstr.h"
7 #include "libsmw.h"
8 #include "asar.h"
9 #include "virtualfile.h"
10 #include "assembleblock.h"
11 #include "macro.h"
12 #include "asar_math.h"
13 #include "warnings.h"
14 #include <math.h>
15 #include <functional>
16 #include <algorithm>
17
18 bool math_pri=true;
19 bool math_round=false;
20 extern bool suppress_all_warnings;
21
22 static const char * str;
23 //save before calling eval if needed after
24 static const char * current_user_function_name;
25
26 static double getnumcore();
27 static double getnum();
28 static double eval(int depth);
29
30 //label (bool foundLabel) (bool forwardLabel)
31 //userfunction
32
33 bool foundlabel;
34 bool foundlabel_static;
35 bool forwardlabel;
36
37 struct cachedfile {
38 string filename;
39 virtual_file_handle filehandle;
40 size_t filesize;
41 bool used;
42 };
43
44 #define numcachedfiles 16
45
46 static cachedfile cachedfiles[numcachedfiles];
47 static int cachedfileindex = 0;
48
49 // Opens a file, trying to open it from cache first
50
51 42 static cachedfile * opencachedfile(string fname, bool should_error)
52 {
53 cachedfile * cachedfilehandle = nullptr;
54
55 // RPG Hacker: Only using a combined path here because that should
56 // hopefully result in a unique string for every file, whereas
57 // fname could be a relative path, which isn't guaranteed to be unique.
58 // Note that this does not affect how we open the file - this is
59 // handled by the filesystem and uses our include paths etc.
60
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 string combinedname = filesystem->create_absolute_path(dir(thisfilename), fname);
61
62
2/2
✓ Branch 0 taken 266 times.
✓ Branch 1 taken 14 times.
280 for (int i = 0; i < numcachedfiles; i++)
63 {
64
4/4
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 214 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 24 times.
266 if (cachedfiles[i].used && cachedfiles[i].filename == combinedname)
65 {
66 28 cachedfilehandle = &cachedfiles[i];
67 break;
68 }
69 }
70
71 if (cachedfilehandle == nullptr)
72 {
73
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (cachedfiles[cachedfileindex].used)
75 {
76 filesystem->close_file(cachedfiles[cachedfileindex].filehandle);
77 cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
78 cachedfiles[cachedfileindex].used = false;
79 }
80
81 14 cachedfilehandle = &cachedfiles[cachedfileindex];
82 }
83
84 if (cachedfilehandle != nullptr)
85 {
86
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 28 times.
42 if (!cachedfilehandle->used)
87 {
88
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 cachedfilehandle->filehandle = filesystem->open_file(fname, thisfilename);
89
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE)
90 {
91 4 cachedfilehandle->used = true;
92 4 cachedfilehandle->filename = combinedname;
93
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 cachedfilehandle->filesize = filesystem->get_file_size(cachedfilehandle->filehandle);
94 4 cachedfileindex++;
95 // randomdude999: when we run out of cached files, just start overwriting ones from the start
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (cachedfileindex >= numcachedfiles) cachedfileindex = 0;
97 }
98 }
99 }
100
101
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
42 if ((cachedfilehandle == nullptr || cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && should_error)
102 {
103 asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), fname.data());
104 }
105
106 42 return cachedfilehandle;
107 42 }
108
109
110 188 void closecachedfiles()
111 {
112
2/2
✓ Branch 0 taken 3008 times.
✓ Branch 1 taken 188 times.
3196 for (int i = 0; i < numcachedfiles; i++)
113 {
114
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3004 times.
3008 if (cachedfiles[i].used)
115 {
116
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE)
117 {
118 4 filesystem->close_file(cachedfiles[i].filehandle);
119 4 cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
120 }
121
122 4 cachedfiles[i].used = false;
123 }
124 }
125
126 188 cachedfileindex = 0;
127 188 }
128
129
130 8 static int struct_size(const char *name)
131 {
132
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
133
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 else if(!structs.exists(name)) return 0;
134 8 return structs.find(name).struct_size;
135 }
136
137 38 static int object_size(const char *name)
138 {
139
3/4
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 30 times.
38 if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
140
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 else if(!structs.exists(name)) return 0;
141 38 return structs.find(name).object_size;
142 }
143
144 10 static int data_size(const char *name)
145 {
146 unsigned int label;
147 10 unsigned int next_label = 0xFFFFFF;
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if(!labels.exists(name)) asar_throw_error(2, error_type_block, error_id_label_not_found, name);
149 10 foundlabel = true;
150 10 snes_label label_data = labels.find(name);
151 10 foundlabel_static &= label_data.is_static;
152 10 label = label_data.pos & 0xFFFFFF;
153 10 labels.each([&next_label, label](const char *key, snes_label current_label){
154 30 current_label.pos &= 0xFFFFFF;
155
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 2 times.
30 if(label < current_label.pos && current_label.pos < next_label){
156 6 next_label = current_label.pos;
157 }
158 30 });
159
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 if(next_label == 0xFFFFFF) asar_throw_warning(2, warning_id_datasize_last_label, name);
160
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 if(next_label-label > 0xFFFF) asar_throw_warning(2, warning_id_datasize_exceeds_size, name);
161 10 return next_label-label;
162 }
163
164
165 148 string get_string_argument()
166 {
167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 148 times.
148 while (*str==' ') str++;
168
1/2
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
148 if (*str=='"')
169 {
170 const char * strpos = str;
171 148 str++;
172
3/4
✓ Branch 0 taken 1894 times.
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 1894 times.
✗ Branch 3 not taken.
2042 while (*str!='"' && *str!='\0' && *str!='\n') str++;
173
1/2
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
148 if (*str == '"')
174 {
175 148 string tempname(strpos, (int)(str - strpos + 1));
176 148 str++;
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 148 times.
148 while (*str==' ') str++; //eat space
178
1/2
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
148 return string(safedequote(tempname.temp_raw()));
179 148 }
180 // RPG Hacker: AFAIK, this is never actually triggered, since unmatched quotes are already detected earlier,
181 // but since it does no harm here, I'll keep it in, just to be safe
182 else asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
183 }//make this error a better one later
184
185 asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
186 return ""; //never actually called, but I don't feel like figuring out __attribute__ ((noreturn)) on MSVC
187 }
188
189 //only returns alphanumeric (and _) starting with alpha or _
190 128 string get_symbol_argument()
191 {
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 while (*str==' ') str++; //is this proper? Dunno yet.
193 const char * strpos = str;
194 // hack: for backwards compat, allow strings as symbols
195
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 124 times.
128 if(*str=='"') {
196 4 asar_throw_warning(2, warning_id_feature_deprecated, "quoted symbolic arguments", "Remove the quotations");
197 4 string arg = get_string_argument();
198 int i = 0;
199
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if(is_alpha(arg[i]) || arg[i] == '_') i++;
200
5/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
28 while(is_alnum(arg[i]) || arg[i] == '_' || arg[i] == '.') i++;
201
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if(arg[i] != '\0') asar_throw_error(2, error_type_block, error_id_invalid_label_name);
202 4 return arg;
203 4 }
204
3/4
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
124 if(is_alpha(*str) || *str == '_') str++;
205
6/6
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 446 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 438 times.
✓ Branch 4 taken 314 times.
✓ Branch 5 taken 124 times.
588 while (is_alnum(*str) || *str == '_' || *str == '.') str++;
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if(strpos == str){
207 //error nothing was read, this is a placeholder error
208 asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
209 }
210
211 124 string symbol = string(strpos, (int)(str - strpos));
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 while (*str==' ') str++; //eat spaces
213 124 return symbol;
214 124 }
215
216 284 double get_double_argument()
217 {
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 284 times.
284 while (*str==' ') str++;
219 284 double result = eval(0);
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 284 times.
284 while (*str==' ') str++; //eat spaces
221 284 return result;
222 }
223
224 //will eat the comma
225 49 bool has_next_parameter()
226 {
227
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 34 times.
49 if (*str==',')
228 {
229 15 str++;
230 15 return true;
231 }
232 return false;
233 }
234
235 117 void require_next_parameter()
236 {
237
1/2
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
117 if (*str==',')
238 {
239 117 str++;
240 117 return;
241 }
242 asar_throw_error(2, error_type_block, error_id_require_parameter);
243 }
244
245 2 template <typename F> double asar_unary_wrapper()
246 {
247 2 return F()(get_double_argument());
248 }
249
250 4 template <double (*F)(double)> double asar_unary_wrapper()
251 {
252 4 return F(get_double_argument());
253 }
254
255 //possibly restrict type T in the future....
256 //first a case for functors
257 44 template <typename F> double asar_binary_wrapper()
258 {
259 44 double first = get_double_argument();
260 44 require_next_parameter();
261
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
44 return F()(first, get_double_argument());
262 }
263 //this could be DRY with if constexpr....oh well
264 26 template <double (*F)(double, double)> double asar_binary_wrapper()
265 {
266 26 double first = get_double_argument();
267 26 require_next_parameter();
268 26 return F(first, get_double_argument());
269 }
270
271 double asar_bank(double a)
272 {
273 2 return (int)a >> 16;
274 }
275
276
277 double asar_logical_nand(double a, double b)
278 {
279 3 return !(a && b);
280 }
281
282
283 double asar_logical_nor(double a, double b)
284 {
285 3 return !(a || b);
286 }
287
288
289 double asar_logical_xor(double a, double b)
290 {
291 3 return !!a ^ !!b;
292 }
293
294 double asar_max(double a, double b)
295 {
296
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
5 return a > b ? a : b;
297 }
298
299 double asar_min(double a, double b)
300 {
301
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
5 return a < b ? a : b;
302 }
303
304 3 double asar_clamp()
305 {
306 3 double value = get_double_argument();
307 3 require_next_parameter();
308 3 double low = get_double_argument();
309 3 require_next_parameter();
310 3 double high = get_double_argument();
311
312 3 return asar_max(low, asar_min(high, value));
313 }
314
315 2 double asar_safediv()
316 {
317 2 double dividend = get_double_argument();
318 2 require_next_parameter();
319 2 double divisor = get_double_argument();
320 2 require_next_parameter();
321 2 double default_value = get_double_argument();
322
323
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 return divisor == 0.0 ? default_value : dividend / divisor;
324 }
325
326 11 double asar_select()
327 {
328 11 double selector = get_double_argument();
329 11 require_next_parameter();
330 11 double a = get_double_argument();
331 11 require_next_parameter();
332 11 double b = get_double_argument();
333
334
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 return selector == 0.0 ? b : a;
335 }
336
337 57 double asar_snestopc_wrapper()
338 {
339 57 return snestopc(get_double_argument());
340 }
341
342 41 double asar_pctosnes_wrapper()
343 {
344 41 return pctosnes(get_double_argument());
345 }
346
347 6 double asar_realbase_wrapper()
348 {
349 //need a better way to do this to make sure it is useful...
350 //foundlabel=true; //Going to consider this an implicit label because we don't really have a better system
351 6 return realsnespos;
352 }
353
354 18 template <int count> double asar_read()
355 {
356 18 int target = get_double_argument();
357 18 int addr=snestopc_pick(target);
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
18 if(has_next_parameter())
359 {
360 double default_value = get_double_argument();
361 if (addr<0) return default_value;
362 else if (addr+count>romlen_r) return default_value;
363 }
364 else
365 {
366
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
18 if (addr<0) asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, (hex6((unsigned int)target) + " in read function").data());
367
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
22 else if (addr+count>romlen_r) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, (hex6(target) + " in read function").data());
368 }
369
370 unsigned int value = 0;
371
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
56 for(int i = 0; i < count; i++)
372 {
373 40 value |= romdata_r[addr+i] << (8 * i);
374 }
375 16 return value;
376 }
377
378 24 template <int count> double asar_canread()
379 {
380 int length = count;
381 if(!length)
382 {
383 length = get_double_argument();
384 }
385 24 int addr=snestopc_pick(get_double_argument());
386
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
24 if (addr<0 || addr+length-1>=romlen_r) return 0;
387 12 else return 1;
388 }
389
390 38 template <size_t count> double asar_readfile()
391 {
392 static_assert(count && count <= 4, "invalid count"); //1-4 inclusive
393
394 38 string name = get_string_argument();
395
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
38 require_next_parameter();
396
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
38 size_t offset = get_double_argument();
397 38 bool should_error = !has_next_parameter();
398
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
38 cachedfile * fhandle = opencachedfile(name, should_error);
399
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
38 if(!should_error)
400 {
401
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 double default_value = get_double_argument();
402
3/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 2 times.
18 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return default_value;
403
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
14 if (offset < 0 || offset + count > fhandle->filesize) return default_value;
404 }
405 else
406 {
407
2/10
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
20 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
408
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
20 if (offset < 0 || offset + count > fhandle->filesize) asar_throw_error(2, error_type_block, error_id_file_offset_out_of_bounds, dec(offset).data(), name.data());
409 }
410
411 28 unsigned char data[4] = { 0, 0, 0, 0 };
412
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
28 filesystem->read_file(fhandle->filehandle, data, offset, count);
413
414 unsigned int value = 0;
415
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 14 times.
70 for(size_t i = 0; i < count; i++)
416 {
417 42 value |= data[i] << (8 * i);
418 }
419
420 28 return value;
421 38 }
422
423 18 template <size_t count> double asar_canreadfile()
424 {
425 18 string name = get_string_argument();
426
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 require_next_parameter();
427
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 size_t offset = get_double_argument();
428 size_t length = count;
429 if(!count)
430 {
431
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 require_next_parameter();
432
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 length = get_double_argument();
433 }
434
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 cachedfile * fhandle = opencachedfile(name, false);
435
3/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 2 times.
18 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return 0;
436
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
14 if (offset < 0 || offset + length > fhandle->filesize) return 0;
437 return 1;
438 18 }
439
440 // returns 0 if the file is OK, 1 if the file doesn't exist, 2 if it couldn't be opened for some other reason
441 12 static double asar_filestatus()
442 {
443
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 cachedfile * fhandle = opencachedfile(get_string_argument(), false);
444
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
12 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
445 {
446
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (filesystem->get_last_error() == vfe_doesnt_exist)
447 {
448 return 1;
449 }
450 else
451 {
452 return 2;
453 }
454 }
455 return 0;
456 }
457
458 // Returns the size of the specified file.
459 2 static double asar_filesize()
460 {
461 2 string name = get_string_argument();
462
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 cachedfile * fhandle = opencachedfile(name, false);
463
2/10
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
464 2 return (double)fhandle->filesize;
465 2 }
466
467 // Checks whether the specified define is defined.
468 62 static double asar_isdefined()
469 {
470 62 return defines.exists(get_string_argument());
471 }
472
473 // RPG Hacker: What exactly makes this function overly complicated, you ask?
474 // Well, it converts a double to a string and then back to a double.
475 // It was the quickest reliable solution I could find, though, so there's that.
476 2 static double asar_round()
477 {
478 2 double number = get_double_argument();
479 2 require_next_parameter();
480
481 // Hue hue hue... ass!
482 // OK, sorry, I apologize.
483 2 string asstring = ftostrvar(number, get_double_argument());
484
485 // Some hacky shenanigans with variables going on here
486
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 const char * strbackup = str;
487 2 str = asstring;
488
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 double asdouble = (double)getnum();
489 2 str = strbackup;
490
491 2 return asdouble;
492 2 }
493
494 110 static double asar_structsize_wrapper()
495 {
496 110 string symbol = get_symbol_argument();
497
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 8 times.
110 if(symbol == "..."){
498
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 101 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
102 if(!inmacro) asar_throw_error(2, error_type_block, error_id_vararg_sizeof_nomacro);
499
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
101 if(numvarargs == -1) asar_throw_error(2, error_type_block, error_id_macro_not_varadic);
500 100 return numvarargs;
501 }
502
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 return (double)struct_size(symbol);
503 110 }
504
505 8 static double asar_objectsize_wrapper()
506 {
507
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 return (double)object_size(get_symbol_argument());
508 }
509
510 10 static double asar_datasize_wrapper()
511 {
512
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 return (double)data_size(get_symbol_argument());
513 }
514
515 12 static double asar_stringsequal()
516 {
517 12 string string1 = get_string_argument();
518
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 require_next_parameter();
519
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
30 return (strcmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0);
520 12 }
521
522 6 static double asar_stringsequalnocase()
523 {
524 6 string string1 = get_string_argument();
525
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 require_next_parameter();
526
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
12 return (stricmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0);
527 6 }
528
529 21 string copy_arg()
530 {
531
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 17 times.
21 if(*str == '"')
532 {
533 4 string t = "\"";
534
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 return (t += get_string_argument() + "\"");
535 4 }
536
537 17 string result;
538 bool is_symbolic = true;
539 int parlevel=0;
540 int i = 0;
541
5/6
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 15 times.
34 while(parlevel > 0 || (str[i] != ',' && str[i] != ')'))
542 {
543 17 is_symbolic &= is_ualnum(str[i]);
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if(str[i] == '(') parlevel++;
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 else if(str[i] == ')') parlevel--;
546 17 i++;
547 }
548 17 result += string(str, i);
549 17 str += i;
550
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if(!is_symbolic)
552 {
553 const char *oldstr=str;
554 str = (const char *)result;
555 result = ftostr(eval(0));
556 str = oldstr;
557 }
558 17 return result;
559 17 }
560
561 assocarr<double (*)()> builtin_functions =
562 {
563 {"sqrt", asar_unary_wrapper<sqrt>},
564 {"sin", asar_unary_wrapper<sin>},
565 {"cos", asar_unary_wrapper<cos>},
566 {"tan", asar_unary_wrapper<tan>},
567 {"asin", asar_unary_wrapper<asin>},
568 {"acos", asar_unary_wrapper<acos>},
569 {"atan", asar_unary_wrapper<atan>},
570 {"arcsin", asar_unary_wrapper<asin>},
571 {"arccos", asar_unary_wrapper<acos>},
572 {"arctan", asar_unary_wrapper<atan>},
573 {"log", asar_unary_wrapper<log>},
574 {"log10", asar_unary_wrapper<log10>},
575 {"log2", asar_unary_wrapper<log2>},
576
577 {"ceil", asar_unary_wrapper<ceil>},
578 {"floor", asar_unary_wrapper<floor>},
579
580 {"read1", asar_read<1>}, //This handles the safe and unsafe variant
581 {"read2", asar_read<2>},
582 {"read3", asar_read<3>},
583 {"read4", asar_read<4>},
584 {"canread", asar_canread<0>},
585 {"canread1", asar_canread<1>},
586 {"canread2", asar_canread<2>},
587 {"canread3", asar_canread<3>},
588 {"canread4", asar_canread<4>},
589
590 {"readfile1", asar_readfile<1>},
591 {"readfile2", asar_readfile<2>},
592 {"readfile3", asar_readfile<3>},
593 {"readfile4", asar_readfile<4>},
594 {"canreadfile", asar_canreadfile<0>},
595 {"canreadfile1", asar_canreadfile<1>},
596 {"canreadfile2", asar_canreadfile<2>},
597 {"canreadfile3", asar_canreadfile<3>},
598 {"canreadfile4", asar_canreadfile<4>},
599
600 {"filesize", asar_filesize},
601 {"getfilestatus", asar_filestatus},
602
603 {"defined", asar_isdefined},
604
605 {"snestopc", asar_snestopc_wrapper},
606 {"pctosnes", asar_pctosnes_wrapper},
607 {"realbase", asar_realbase_wrapper},
608
609 {"max", asar_binary_wrapper<asar_max>},
610 {"min", asar_binary_wrapper<asar_min>},
611 {"clamp", asar_clamp},
612
613 {"safediv", asar_safediv},
614
615 {"select", asar_select},
616 {"bank", asar_unary_wrapper<asar_bank>},
617 {"not", asar_unary_wrapper<std::logical_not<unsigned int>>},
618 {"equal", asar_binary_wrapper<std::equal_to<double>>},
619 {"notequal", asar_binary_wrapper<std::not_equal_to<double>>},
620 {"less", asar_binary_wrapper<std::less<double>>},
621 {"lessequal", asar_binary_wrapper<std::less_equal<double>>},
622 {"greater", asar_binary_wrapper<std::greater<double>>},
623 {"greaterequal", asar_binary_wrapper<std::greater_equal<double>>},
624
625 {"and", asar_binary_wrapper<std::logical_and<unsigned int>>},
626 {"or", asar_binary_wrapper<std::logical_or<unsigned int>>},
627 {"nand", asar_binary_wrapper<asar_logical_nand>},
628 {"nor", asar_binary_wrapper<asar_logical_nor>},
629 {"xor", asar_binary_wrapper<asar_logical_xor>},
630
631 {"round", asar_round},
632
633 {"sizeof", asar_structsize_wrapper},
634 {"objectsize", asar_objectsize_wrapper},
635 {"datasize", asar_datasize_wrapper},
636
637 {"stringsequal", asar_stringsequal},
638 {"stringsequalnocase", asar_stringsequalnocase}
639 };
640
641 assocarr<double (*)()> functions;
642
643 struct funcdat {
644 autoptr<char*> name;
645 int numargs;
646 autoptr<char*> argbuf;//this one isn't used, it's just to free it up
647 autoptr<char**> arguments;
648 autoptr<char*> content;
649 };
650 static assocarr<funcdat> user_functions;
651
652 15 static double asar_call_user_function()
653 {
654 15 autoarray<string> args;
655
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 funcdat &user_function = user_functions[current_user_function_name];
656 15 string real_content;
657
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 while (*str==' ') str++;
659 15 bool has_next = *str != ')';
660
661
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 15 times.
36 for (int i=0;i<user_function.numargs;i++)
662 {
663
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if(!has_next)
664 {
665 asar_throw_error(2, error_type_block, error_id_expected_parameter, current_user_function_name);
666 }
667
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
42 args[i] = copy_arg();
668 21 has_next = has_next_parameter();
669 }
670
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if(has_next)
672 {
673 asar_throw_error(2, error_type_block, error_id_unexpected_parameter, current_user_function_name);
674 }
675
676
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 15 times.
77 for(int i=0; user_function.content[i]; i++)
677 {
678
3/4
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
62 if(!is_alpha(user_function.content[i]) && user_function.content[i] != '_')
679 {
680 37 real_content += user_function.content[i];
681 37 continue;
682 }
683 bool found = false;
684
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 25 times.
66 for (int j=0;user_function.arguments[j];j++)
685 {
686 //this should *always* have a null term or another character after
687 41 bool potential_arg = stribegin(user_function.content+i, user_function.arguments[j]);
688 41 int next_char = i+strlen(user_function.arguments[j]);
689
4/6
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
41 if(potential_arg && (!is_alnum(user_function.content[next_char]) && user_function.content[next_char] != '_'))
690 {
691 21 real_content += args[j];
692 21 i = next_char - 1;
693 found = true;
694 }
695 }
696
697
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 21 times.
25 if(!found){
698
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 4 times.
40 for(; is_ualnum(user_function.content[i]); i++){
699 36 real_content += user_function.content[i];
700 }
701 4 real_content += user_function.content[i];
702 }
703 }
704
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 const char * oldstr=str;
705 15 str = (const char *)real_content;
706
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 double result = eval(0);
707 15 str = oldstr;
708 15 return result;
709 15 }
710
711 15 void createuserfunc(const char * name, const char * arguments, const char * content)
712 {
713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!confirmqpar(content)) asar_throw_error(0, error_type_block, error_id_mismatched_parentheses);
714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if(functions.exists(name)) //functions holds both types.
715 {
716 //asar_throw_error(0, error_type_block, error_id_function_redefined, name);
717 asar_throw_warning(1, warning_id_feature_deprecated, "overwriting a previously defined function", "change the function name");
718 }
719 15 funcdat& user_function=user_functions[name];
720 15 user_function.name= duplicate_string(name);
721 15 user_function.argbuf= duplicate_string(arguments);
722 15 user_function.arguments=qsplit(user_function.argbuf, ",", &(user_function.numargs));
723 15 user_function.content= duplicate_string(content);
724
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 15 times.
36 for (int i=0;user_function.arguments[i];i++)
725 {
726
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 21 times.
54 for(int j=0;user_function.arguments[j];j++)
727 {
728
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
33 if(i!=j && !stricmp(user_function.arguments[i], user_function.arguments[j]))
729 {
730 asar_throw_error(0, error_type_block, error_id_duplicate_param_name, user_function.arguments[i], name);
731 }
732 }
733
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (!confirmname(user_function.arguments[i]))
734 {
735 user_functions.remove(name);
736 asar_throw_error(0, error_type_block, error_id_invalid_param_name);
737 }
738 }
739
740 15 functions[name] = asar_call_user_function;
741 15 }
742
743 918844 static double getnumcore()
744 {
745
2/2
✓ Branch 0 taken 268 times.
✓ Branch 1 taken 918576 times.
918844 if (*str=='(')
746 {
747 268 str++;
748 268 double rval=eval(0);
749
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 268 times.
268 if (*str != ')') asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
750 268 str++;
751 268 return rval;
752 }
753
2/2
✓ Branch 0 taken 3449 times.
✓ Branch 1 taken 915127 times.
918576 if (*str=='$')
754 {
755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3449 times.
3449 if (!is_xdigit(str[1])) asar_throw_error(2, error_type_block, error_id_invalid_hex_value);
756
1/2
✓ Branch 0 taken 3449 times.
✗ Branch 1 not taken.
3449 if (to_lower(str[2])=='x') return -42;//let str get an invalid value so it'll throw an invalid operator later on
757 3449 return strtoull(str+1, const_cast<char**>(&str), 16);
758 }
759
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 915103 times.
915127 if (*str=='%')
760 {
761
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (str[1] != '0' && str[1] != '1') asar_throw_error(2, error_type_block, error_id_invalid_binary_value);
762 24 return strtoull(str+1, const_cast<char**>(&str), 2);
763 }
764
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 915079 times.
915103 if (*str=='\'')
765 {
766
2/4
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
24 if (!str[1] || str[2] != '\'') asar_throw_error(2, error_type_block, error_id_invalid_character);
767 24 unsigned int rval=table.table[(unsigned char)str[1]];
768 24 str+=3;
769 24 return rval;
770 }
771
2/2
✓ Branch 0 taken 914183 times.
✓ Branch 1 taken 896 times.
915079 if (is_digit(*str))
772 {
773 const char* end = str;
774
4/4
✓ Branch 0 taken 3199025 times.
✓ Branch 1 taken 914218 times.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 914183 times.
4113243 while (is_digit(*end) || *end == '.') end++;
775 914183 string number;
776 914183 number.assign(str, (int)(end - str));
777 914183 str = end;
778 914183 return atof(number);
779 914183 }
780
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 885 times.
896 if (is_alpha(*str) || *str=='_' || *str=='.' || *str=='?')
781 {
782 893 const char * start=str;
783
6/6
✓ Branch 0 taken 7429 times.
✓ Branch 1 taken 1083 times.
✓ Branch 2 taken 112 times.
✓ Branch 3 taken 971 times.
✓ Branch 4 taken 78 times.
✓ Branch 5 taken 893 times.
8512 while (is_alnum(*str) || *str == '_' || *str == '.') str++;
784 893 int len=(int)(str-start);
785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 893 times.
893 while (*str==' ') str++;
786
2/2
✓ Branch 0 taken 447 times.
✓ Branch 1 taken 446 times.
893 if (*str=='(')
787 {
788 447 str++;
789 // RPG Hacker: This is only here to assure that all strings are still
790 // alive in memory when we call our functions further down
791 double result;
792 while (true)
793 {
794
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 447 times.
447 while (*str==' ') str++;
795 447 string function_name = string(start, len);
796
1/2
✓ Branch 0 taken 447 times.
✗ Branch 1 not taken.
447 if(functions.exists(function_name))
797 {
798 447 current_user_function_name = function_name;
799
3/4
✓ Branch 0 taken 447 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 444 times.
✓ Branch 3 taken 3 times.
447 result = functions[function_name]();
800 }
801 else
802 {
803 str++;
804 break;
805 }
806
807
1/2
✓ Branch 0 taken 444 times.
✗ Branch 1 not taken.
444 if (*str==')')
808 {
809 444 str++;
810 return result;
811 }
812 asar_throw_error(2, error_type_block, error_id_malformed_function_call);
813 447 }
814
815 asar_throw_error(2, error_type_block, error_id_function_not_found, start);
816 }
817 else
818 {
819 446 foundlabel=true;
820
821 const char *old_start = start;
822 446 snes_label label_data = labelval(&start);
823 446 int i=(int)label_data.pos;
824 446 foundlabel_static &= label_data.is_static;
825 bool scope_passed = false;
826 bool subscript_passed = false;
827
2/2
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 446 times.
1192 while (str < start)
828 {
829
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 268 times.
300 if (*str == '.') scope_passed = true;
830
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 270 times.
300 if (*(str++) == '[')
831 {
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (subscript_passed)
833 {
834 asar_throw_error(2, error_type_block, error_id_multiple_subscript_operators);
835 break;
836 }
837 subscript_passed = true;
838
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (scope_passed)
839 {
840 asar_throw_error(2, error_type_block, error_id_invalid_subscript);
841 break;
842 }
843 30 string struct_name = substr(old_start, (int)(str - old_start - 1));
844
2/4
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 i += (int)(eval(0) * object_size(struct_name));
845 30 }
846 }
847
848 446 str=start;
849
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 444 times.
446 if (i==-1) forwardlabel=true;
850 446 return (int)i&0xFFFFFF;
851 }
852 }
853 3 asar_throw_error(2, error_type_block, error_id_invalid_number);
854 return 0.0;
855 }
856
857 1129607 static double sanitize(double val)
858 {
859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1129607 times.
1129607 if (val != val) asar_throw_error(2, error_type_block, error_id_nan);
860
3/4
✓ Branch 0 taken 1129607 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1127142 times.
✓ Branch 3 taken 2465 times.
1129607 if (math_round && !default_math_round_off) return trunc(val); // originally used int cast, but that broke numbers > $8000_0000
861 return val;
862 }
863
864 918878 static double getnum()
865 {
866
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 918878 times.
918878 while (*str==' ') str++;
867 #define prefix(sym, func) if (*str == sym) { str+=1; double val=getnum(); return sanitize(func); }
868 #define prefix_dep(sym, func) if (*str == sym) { str+=1; asar_throw_warning(2, warning_id_feature_deprecated, "xkas style numbers ", "remove the #"); double val=getnum(); return sanitize(func); }
869 #define prefix2(sym, sym2, func) if (*str == sym && *(str+1) == sym2) { str+=2; double val=getnum(); return sanitize(func); }
870
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 918850 times.
918878 prefix('-', -val);
871
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 918848 times.
918850 prefix('~', ~(int)val);
872
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 918844 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
918848 prefix2('<', ':', (int)val>>16);
873
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 918844 times.
918844 prefix('+', val);
874
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 918841 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
918844 prefix_dep('#' && emulatexkas, val);
875 #undef prefix
876 918844 return sanitize(getnumcore());
877 }
878
879 37573 int64_t getnum(const char* instr)
880 {
881 37573 return getnum64(instr);
882
883 // randomdude999: perform manual bounds-checking and 2's complement,
884 // to prevent depending on UB
885 double num = math(instr);
886 if(num < 0) {
887 // manual 2's complement
888 if((-num) > (double)UINT32_MAX) {
889 // out of bounds, return closest inbounds value
890 // (this value is the most negative value possible)
891 return ((uint32_t)INT32_MAX)+1;
892 }
893 return ~((uint32_t)(-num))+1;
894 } else {
895 if(num > (double)UINT32_MAX) {
896 // out of bounds, return closest inbounds value
897 return UINT32_MAX;
898 }
899 return (uint32_t)num;
900 }
901 }
902
903 37777 int64_t getnum64(const char* instr)
904 {
905 // randomdude999: perform manual bounds-checking
906 // to prevent depending on UB
907 37777 double num = math(instr);
908
1/2
✓ Branch 0 taken 37765 times.
✗ Branch 1 not taken.
37765 if(num < (double)INT64_MIN) {
909 return INT64_MIN;
910
1/2
✓ Branch 0 taken 37765 times.
✗ Branch 1 not taken.
37765 } else if(num > (double)INT64_MAX) {
911 return INT64_MAX;
912 }
913 37765 return (int64_t)num;
914 }
915
916 // RPG Hacker: Same function as above, but doesn't truncate our number via int conversion
917 316776 double getnumdouble(const char * instr)
918 {
919 316776 return math(instr);
920 }
921
922
923 static double oper_wrapped_throw(asar_error_id errid)
924 {
925 asar_throw_error(2, error_type_block, errid);
926 return 0.0;
927 }
928
929 813694 static double eval(int depth)
930 {
931 813694 const char* posneglabel = str;
932 813694 string posnegname = posneglabelname(&posneglabel, false);
933
934
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 813638 times.
813694 if (posnegname.length() > 0)
935 {
936
3/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
56 if (*posneglabel != '\0' && *posneglabel != ')') goto notposneglabel;
937
938 28 str = posneglabel;
939
940 28 foundlabel=true;
941
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 4 times.
28 if (*(posneglabel-1) == '+') forwardlabel=true;
942
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 snes_label label_data = labelval(posnegname);
943 28 foundlabel_static &= label_data.is_static;
944 28 return label_data.pos & 0xFFFFFF;
945 }
946 813638 notposneglabel:
947
1/2
✓ Branch 0 taken 813666 times.
✗ Branch 1 not taken.
1627332 recurseblock rec;
948
2/2
✓ Branch 0 taken 813660 times.
✓ Branch 1 taken 6 times.
813666 double left=getnum();
949 double right;
950
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 813660 times.
813660 while (*str==' ') str++;
951
6/6
✓ Branch 0 taken 210854 times.
✓ Branch 1 taken 813541 times.
✓ Branch 2 taken 210774 times.
✓ Branch 3 taken 80 times.
✓ Branch 4 taken 210744 times.
✓ Branch 5 taken 30 times.
1024395 while (*str && *str != ')' && *str != ','&& *str != ']')
952 {
953
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210744 times.
210744 while (*str==' ') str++;
954 // why was this an int cast
955
3/4
✓ Branch 0 taken 210744 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 210361 times.
✓ Branch 3 taken 383 times.
210744 if (math_round && !default_math_round_off) left=trunc(left);
956 #define oper(name, thisdepth, contents) \
957 if (!strncmp(str, name, strlen(name))) \
958 { \
959 if (math_pri || default_math_pri) \
960 { \
961 if (depth<=thisdepth) \
962 { \
963 str+=strlen(name); \
964 right=eval(thisdepth+1); \
965 } \
966 else return left; \
967 } \
968 else \
969 { \
970 str+=strlen(name); \
971 right=getnum(); \
972 } \
973 left=sanitize(contents); \
974 continue; \
975 }
976
9/14
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 210738 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
210744 oper("**", 4, pow((double)left, (double)right));
977
10/14
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 210726 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 12 times.
✗ Branch 13 not taken.
210738 oper("*", 3, left*right);
978
10/18
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 210720 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 6 times.
✗ Branch 17 not taken.
210726 oper("/", 3, right != 0.0 ? left / right : oper_wrapped_throw(error_id_division_by_zero));
979
1/18
✗ Branch 0 not taken.
✓ Branch 1 taken 210720 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
210720 oper("%", 3, right != 0.0 ? fmod((double)left, (double)right) : oper_wrapped_throw(error_id_modulo_by_zero));
980
11/14
✓ Branch 0 taken 210628 times.
✓ Branch 1 taken 92 times.
✓ Branch 2 taken 105478 times.
✓ Branch 3 taken 105150 times.
✓ Branch 4 taken 334 times.
✓ Branch 5 taken 105144 times.
✓ Branch 6 taken 105478 times.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 105478 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 105144 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 210622 times.
✗ Branch 13 not taken.
210720 oper("+", 2, left+right);
981
10/14
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 22 times.
✓ Branch 6 taken 67 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 67 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 22 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 89 times.
✗ Branch 13 not taken.
92 oper("-", 2, left-right);
982
1/18
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
3 oper("<<", 1, right >= 0.0 ? (int64_t)left<<(uint64_t)right : oper_wrapped_throw(error_id_negative_shift));
983
1/18
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
3 oper(">>", 1, right >= 0.0 ? (int64_t)left>>(uint64_t)right : oper_wrapped_throw(error_id_negative_shift));
984
1/14
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
3 oper("&", 0, (int64_t)left&(int64_t)right);
985
1/14
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
3 oper("|", 0, (int64_t)left|(int64_t)right);
986
1/14
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
3 oper("^", 0, (int64_t)left^(int64_t)right);
987
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 asar_throw_error(2, error_type_block, error_id_unknown_operator);
988 #undef oper
989 }
990 return left;
991 813694 }
992
993 //static autoptr<char*> freeme;
994 354553 double math(const char * s)
995 {
996 //free(freeme);
997 //freeme=NULL;
998 354553 foundlabel=false;
999 354553 foundlabel_static=true;
1000 354553 forwardlabel=false;
1001 double rval;
1002
1003
3/4
✓ Branch 0 taken 354553 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1559 times.
✓ Branch 3 taken 352994 times.
354553 if(math_pri || default_math_pri)
1004 {
1005 1559 str = s;
1006 1559 rval = eval(0);
1007 }
1008 else
1009 {
1010 352994 str = s;
1011 352994 rval = eval(0);
1012
1013 double pri_rval = NAN;
1014
1015 352985 suppress_all_warnings = true;
1016 352985 math_pri = true;
1017 try
1018 {
1019 352985 str = s;
1020
1/2
✓ Branch 0 taken 352985 times.
✗ Branch 1 not taken.
352985 pri_rval = eval(0);
1021 }
1022 catch (errfatal&) {}
1023 352985 suppress_all_warnings = false;
1024 352985 math_pri = false;
1025
1026
1/2
✓ Branch 0 taken 352985 times.
✗ Branch 1 not taken.
352985 if (pri_rval != rval)
1027 {
1028 asar_throw_warning(2, warning_id_feature_deprecated, "xkas style left to right math ", "apply order of operations");
1029 }
1030 }
1031
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 354541 times.
354544 if (*str)
1032 {
1033
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (*str == ',') asar_throw_error(2, error_type_block, error_id_invalid_input);
1034 3 else asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
1035 }
1036 354541 return rval;
1037 }
1038
1039 281 void initmathcore()
1040 {
1041 281 functions.reset();
1042 281 builtin_functions.each([](const char* key, double (*val)()) {
1043 17703 functions[key] = val;
1044
1/2
✓ Branch 0 taken 17703 times.
✗ Branch 1 not taken.
35406 functions[STR "_" + key] = val;
1045 17703 });
1046 281 user_functions.reset();
1047 281 }
1048
1049 279 void deinitmathcore()
1050 {
1051 //not needed
1052 279 }
1053