asar coverage - build #259


src/asar/
File: src/asar/asar_math.cpp
Date: 2025-02-26 19:38:12
Lines:
398/450
88.4%
Functions:
88/109
80.7%
Branches:
483/830
58.2%

Line Branch Exec Source
1 #include "platform/file-helpers.h"
2 #include "asar.h"
3 #include "virtualfile.h"
4 #include "assembleblock.h"
5 #include "macro.h"
6 #include "asar_math.h"
7 #include "table.h"
8 #include "unicode.h"
9 #include <cmath>
10
11 #include "math_ast.h"
12
13
14 bool foundlabel;
15 // WARNING: this flag is only correctly set in pass 0, as forward labels are
16 // always non-static but we don't know when we hit forward-labels past pass 0
17 bool foundlabel_static;
18 // only set in pass 0
19 bool forwardlabel;
20
21 struct cachedfile {
22 string filename;
23 virtual_file_handle filehandle;
24 size_t filesize;
25 bool used;
26 };
27
28 #define numcachedfiles 16
29
30 static cachedfile cachedfiles[numcachedfiles];
31 static int cachedfileindex = 0;
32
33 // Opens a file, trying to open it from cache first
34
35 168 static cachedfile * opencachedfile(string fname, bool should_error)
36 {
37 84 cachedfile * cachedfilehandle = nullptr;
38
39
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
168 const char* current_file = get_current_file_name();
40
41 // RPG Hacker: Only using a combined path here because that should
42 // hopefully result in a unique string for every file, whereas
43 // fname could be a relative path, which isn't guaranteed to be unique.
44 // Note that this does not affect how we open the file - this is
45 // handled by the filesystem and uses our include paths etc.
46
2/4
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
168 string combinedname = filesystem->create_absolute_path(dir(current_file), fname);
47
48
2/2
✓ Branch 0 taken 1050 times.
✓ Branch 1 taken 54 times.
1104 for (int i = 0; i < numcachedfiles; i++)
49 {
50
6/6
✓ Branch 0 taken 222 times.
✓ Branch 1 taken 828 times.
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 108 times.
✓ Branch 4 taken 57 times.
✓ Branch 5 taken 468 times.
1050 if (cachedfiles[i].used && cachedfiles[i].filename == combinedname)
51 {
52 114 cachedfilehandle = &cachedfiles[i];
53 57 break;
54 }
55 }
56
57
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 57 times.
84 if (cachedfilehandle == nullptr)
58 {
59
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (cachedfiles[cachedfileindex].used)
60 {
61 filesystem->close_file(cachedfiles[cachedfileindex].filehandle);
62 cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
63 cachedfiles[cachedfileindex].used = false;
64 }
65
66 54 cachedfilehandle = &cachedfiles[cachedfileindex];
67 }
68
69
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 if (cachedfilehandle != nullptr)
70 {
71
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 114 times.
168 if (!cachedfilehandle->used)
72 {
73
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 cachedfilehandle->filehandle = filesystem->open_file(fname, current_file);
74
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 30 times.
54 if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE)
75 {
76 24 cachedfilehandle->used = true;
77
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 cachedfilehandle->filename = combinedname;
78
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 cachedfilehandle->filesize = filesystem->get_file_size(cachedfilehandle->filehandle);
79 24 cachedfileindex++;
80 // randomdude999: when we run out of cached files, just start overwriting ones from the start
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (cachedfileindex >= numcachedfiles) cachedfileindex = 0;
82 }
83 }
84 }
85
86
5/6
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 69 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 84 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
168 if ((cachedfilehandle == nullptr || cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && should_error)
87 {
88 asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), fname.data());
89 }
90
91 168 return cachedfilehandle;
92 168 }
93
94
95 1543 void closecachedfiles()
96 {
97
2/2
✓ Branch 0 taken 24688 times.
✓ Branch 1 taken 1543 times.
26231 for (int i = 0; i < numcachedfiles; i++)
98 {
99
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24664 times.
24688 if (cachedfiles[i].used)
100 {
101
2/4
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE && filesystem)
102 {
103 24 filesystem->close_file(cachedfiles[i].filehandle);
104 24 cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
105 }
106
107 24 cachedfiles[i].used = false;
108 }
109 }
110
111 1543 cachedfileindex = 0;
112 1543 }
113
114
115 18 static int struct_size(const char *name)
116 {
117
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
18 if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
118
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 else if(!structs.exists(name)) return 0;
119 18 return structs.find(name).struct_size;
120 }
121
122 120 static int object_size(const char *name)
123 {
124
4/6
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 96 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 60 times.
120 if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
125
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
120 else if(!structs.exists(name)) return 0;
126 120 return structs.find(name).object_size;
127 }
128
129 30 static int data_size(const char *name)
130 {
131 int label;
132
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
30 if(!labels.exists(name)) asar_throw_error(2, error_type_block, error_id_label_not_found, name);
133 // TODO figure out the proper spot to set these
134 30 foundlabel = true;
135
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 snes_label label_data = labels.find(name);
136 30 foundlabel_static &= label_data.is_static;
137
138 15 label = label_data.id;
139 15 snes_label selected_label;
140 30 selected_label.id = 0xFFFFFF;
141 30 selected_label.pos = 0xFFFFFF;
142 30 labels.each([&selected_label, label](const char *key, snes_label current_label){
143
3/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
90 if(label < current_label.id && current_label.id < selected_label.id){
144 24 selected_label = current_label;
145 }
146 90 });
147
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
30 if(selected_label.id == 0xFFFFFF) asar_throw_warning(2, warning_id_datasize_last_label, name);
148
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
30 if(selected_label.pos-label_data.pos > 0xFFFF) asar_throw_warning(2, warning_id_datasize_exceeds_size, name);
149 30 return selected_label.pos-label_data.pos;
150 }
151
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1182 times.
2364 static void assert_argc(const std::vector<math_val>& args, int expected_args) {
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2364 times.
2364 if(args.size() != expected_args) {
154 asar_throw_error(2, error_type_block, error_id_argument_count, expected_args, (int)args.size());
155 }
156 2364 }
157
158 template<double (*F)(double)>
159 math_val fn_unary_real(const std::vector<math_val>& args) {
160 assert_argc(args, 1);
161 double val = F(args[0].get_double());
162 if (val != val) asar_throw_error(2, error_type_block, error_id_nan);
163 return val;
164 };
165
166 template<double (*F)(double)>
167 math_val fn_rounding(const std::vector<math_val>& args) {
168 assert_argc(args, 1);
169 double val = F(args[0].get_double());
170 // TODO where should we check this? math_val(double) constructor maybe?
171 if (val != val) asar_throw_error(2, error_type_block, error_id_nan);
172 return float_to_int(val);
173 };
174
175 template<math_binop_type OP>
176 192 math_val math_binop_function(const std::vector<math_val>& args) {
177 192 assert_argc(args, 2);
178
3/6
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
288 return evaluate_binop(args[0], args[1], OP);
179 }
180
181 template<bool (*F)(bool, bool)>
182 180 math_val math_bool_binop_function(const std::vector<math_val>& args) {
183 180 assert_argc(args, 2);
184 180 return (int64_t)F(args[0].get_bool(), args[1].get_bool());
185 }
186
187
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
18 static bool fn_and(bool a, bool b) { return a && b; }
188
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
18 static bool fn_or(bool a, bool b) { return a || b; }
189
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
18 static bool fn_nand(bool a, bool b) { return !(a && b); }
190
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
18 static bool fn_nor(bool a, bool b) { return !(a || b); }
191 18 static bool fn_xor(bool a, bool b) { return a ^ b; }
192
193 24 static math_val fn_select(math_val cond, math_val a, math_val b) {
194
4/4
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 21 times.
48 return cond.get_bool() ? a : b;
195 }
196
197 template<int (*F)(int)>
198 600 math_val fn_snes_pc(math_val arg) {
199 600 return (int64_t)F(arg.get_integer());
200 }
201
202 template<int& variable>
203 90 math_val fn_pc_realbase() {
204 135 return (int64_t)variable;
205 }
206
207 6 static math_val fn_bank(math_val arg) {
208 6 return (arg.get_integer() >> 16);
209 }
210
211 12 static math_val fn_safediv(math_val a, math_val b, math_val default_) {
212
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if(b.get_double() == 0.0) return default_;
213
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
9 return evaluate_binop(a, b, math_binop_type::div);
214 }
215
216 48 static math_val fn_not(math_val arg) {
217 48 return (int64_t)!arg.get_bool();
218 }
219
220 template<int (*F)(const char*, const char*)>
221 108 math_val fn_str_eq(math_val va, math_val vb) {
222 108 const string& a = va.get_str();
223 108 const string& b = vb.get_str();
224 108 bool result = F(a.data(), b.data()) == 0;
225 108 return (int64_t)result;
226 }
227
228 static math_val fn_char(math_val str_, math_val ind_) {
229 const string& s = str_.get_str();
230 int64_t ind = ind_.get_integer();
231 if(ind < 0 || ind > s.length())
232 asar_throw_error(2, error_type_block, error_id_oob, (int)ind, s.length());
233 return (int64_t)(unsigned char)s[ind];
234 }
235 math_val fn_strlen(math_val val) {
236 return (int64_t)val.get_str().length();
237 }
238
239 template<int count>
240
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
132 math_val fn_read(const std::vector<math_val>& args) {
241
3/6
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 66 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 33 times.
132 if(args.size() < 1 || args.size() > 2) {
242 // TODO expected amount should be string to show the range
243 asar_throw_error(2, error_type_block, error_id_argument_count, 2, (int)args.size());
244 }
245
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
132 int64_t target = args[0].get_integer();
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
132 int addr = snestopc(target);
247
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
132 if(args.size() == 2) {
249 math_val default_val = args[1];
250 if(addr < 0) return default_val;
251 if(addr + count > romlen_r) return default_val;
252 } else {
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
132 if (addr < 0)
254 asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, (hex((unsigned int)target, 6) + " in read function").data());
255
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 60 times.
132 else if (addr + count > romlen_r)
256
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
30 asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, (hex(target, 6) + " in read function").data());
257 }
258
259 60 int64_t value = 0;
260
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 60 times.
312 for(int i = 0; i < count; i++)
261 {
262 192 value |= romdata_r[addr+i] << (8 * i);
263 }
264 60 return value;
265 }
266 template<int count>
267 216 math_val fn_canread(const std::vector<math_val>& args) {
268 108 int64_t length = count;
269 int64_t addr;
270 if(count == 0) {
271 assert_argc(args, 2);
272 length = args[0].get_integer();
273 addr = args[1].get_integer();
274 } else {
275 216 assert_argc(args, 1);
276 216 addr = args[0].get_integer();
277 }
278 216 int addr_pc = snestopc(addr);
279
3/4
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 54 times.
216 if (addr_pc<0 || addr_pc+length-1>=romlen_r) return (int64_t)0;
280 54 else return (int64_t)1;
281 }
282
283 template<int count>
284
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
168 math_val fn_readfile(const std::vector<math_val>& args) {
285
3/6
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 42 times.
168 if(args.size() < 2 || args.size() > 3) {
286 // TODO expected amount should be string to show the range
287 asar_throw_error(2, error_type_block, error_id_argument_count, 3, (int)args.size());
288 }
289
2/4
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
168 string fname = args[0].get_str();
290
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
168 int64_t offset = args[1].get_integer();
291 168 bool should_error = args.size() == 2;
292
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
168 cachedfile * fhandle = opencachedfile(fname, should_error);
293
294
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 48 times.
168 if(!should_error) {
295
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
36 math_val default_val = args[2];
296
3/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 30 times.
72 if(fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return default_val;
297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
60 if(offset < 0) return default_val;
298
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
60 if(offset + count > fhandle->filesize) return default_val;
299
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
36 } else {
300
2/4
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
96 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
301 asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), fname.data());
302
2/4
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
96 if (offset < 0 || offset + count > fhandle->filesize)
303 asar_throw_error(2, error_type_block, error_id_file_offset_out_of_bounds, dec(offset).data(), fname.data());
304 }
305
306 132 unsigned char data[4] = { 0, 0, 0, 0 };
307
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
132 filesystem->read_file(fhandle->filehandle, data, offset, count);
308
309 66 int64_t value = 0;
310
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 66 times.
348 for(size_t i = 0; i < count; i++)
311 {
312 216 value |= data[i] << (8 * i);
313 }
314
315 66 return value;
316 168 }
317
318 template<int count>
319
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
84 math_val fn_canreadfile(const std::vector<math_val>& args) {
320 42 string fname;
321 42 int64_t length = count;
322 int64_t offset;
323 if(count == 0) {
324
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 assert_argc(args, 3);
325
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
24 fname = args[0].get_str();
326
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 offset = args[1].get_integer();
327
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 length = args[2].get_integer();
328 } else {
329
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
60 assert_argc(args, 2);
330
2/4
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
60 fname = args[0].get_str();
331
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
60 offset = args[1].get_integer();
332 }
333
334
2/4
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
84 cachedfile * fhandle = opencachedfile(fname, false);
335
3/4
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 36 times.
84 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return (int64_t)0;
336
3/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 24 times.
72 if (offset < 0 || offset + length > fhandle->filesize) return (int64_t)0;
337 24 return (int64_t)1;
338 84 }
339
340 12 static math_val fn_min(math_val a, math_val b) {
341
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
18 math_val cond = evaluate_binop(a, b, math_binop_type::comp_lt);
342
4/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
24 return cond.get_bool() ? a : b;
343 6 }
344 12 static math_val fn_max(math_val a, math_val b) {
345
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
18 math_val cond = evaluate_binop(a, b, math_binop_type::comp_gt);
346
4/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
24 return cond.get_bool() ? a : b;
347 6 }
348 18 static math_val fn_clamp(math_val val, math_val lo, math_val hi) {
349 // compute temp = min(hi, val)
350
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
27 math_val cond1 = evaluate_binop(hi, val, math_binop_type::comp_lt);
351
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
18 math_val temp = cond1.get_bool() ? hi : val;
352 // compute res = max(lo, temp)
353
3/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
27 math_val cond2 = evaluate_binop(lo, temp, math_binop_type::comp_gt);
354
4/6
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
36 return cond2.get_bool() ? lo : temp;
355 9 }
356
357 12 static math_val fn_round(math_val val, math_val precision) {
358 // i used to hate the float->str->float approach, but the
359 // alternatives do end up quite messy if implemented properly
360
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
12 string as_str = ftostrvar(val.get_double(), precision.get_integer());
361 12 double res = std::atof(as_str);
362 18 return res;
363 12 }
364 156 static math_val fn_isdefined(math_val defname) {
365 156 return (int64_t)defines.exists(defname.get_str());
366 }
367
368 6 static math_val fn_filesize(math_val fname) {
369
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
6 string name = fname.get_str();
370
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
6 cachedfile * fhandle = opencachedfile(name, false);
371
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
372 asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
373 9 return (int64_t)fhandle->filesize;
374 6 }
375
376 36 static math_val fn_filestatus(math_val fname) {
377
3/6
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
54 cachedfile * fhandle = opencachedfile(fname.get_str(), false);
378
3/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 18 times.
36 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) {
379
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (filesystem->get_last_error() == vfe_doesnt_exist)
380 9 return (int64_t)1;
381 else return (int64_t)2;
382 }
383 9 return (int64_t)0;
384 }
385
386 1068 static math_val fn_structsize_wrapper(math_val val) {
387
2/4
✓ Branch 0 taken 534 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 534 times.
✗ Branch 3 not taken.
1068 string symbol = val.get_identifier();
388 // TODO: do we have a better spot where to parse this...?
389
2/2
✓ Branch 0 taken 1050 times.
✓ Branch 1 taken 18 times.
1068 if(symbol == "..."){
390
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1044 times.
1050 if(!inmacro) asar_throw_error(2, error_type_block, error_id_vararg_sizeof_nomacro);
391
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1038 times.
1044 if(numvarargs == -1) asar_throw_error(2, error_type_block, error_id_macro_not_varadic, "sizeof(...)");
392 1038 return (int64_t)numvarargs;
393 }
394
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 return (int64_t)struct_size(symbol);
395 1068 }
396 120 static math_val fn_objectsize_wrapper(math_val val) {
397 120 return (int64_t)object_size(val.get_identifier());
398 }
399 30 static math_val fn_datasize_wrapper(math_val val) {
400 30 return (int64_t)data_size(val.get_identifier());
401 }
402
403 // these templates could be implemented in a fully generic way too (without
404 // specializing per argument count), but that looks a little too cryptic to me.
405 template<math_val (*F)()>
406 180 math_val fixed_arity(const std::vector<math_val>& args) {
407 180 assert_argc(args, 0);
408 180 return F();
409 }
410 template<math_val (*F)(math_val)>
411 3540 math_val fixed_arity(const std::vector<math_val>& args) {
412 3540 assert_argc(args, 1);
413
4/4
✓ Branch 0 taken 1764 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 879 times.
✓ Branch 3 taken 6 times.
5298 return F(args[0]);
414 }
415 template<math_val (*F)(math_val,math_val)>
416 180 math_val fixed_arity(const std::vector<math_val>& args) {
417 180 assert_argc(args, 2);
418
3/6
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 45 times.
✗ Branch 5 not taken.
270 return F(args[0], args[1]);
419 }
420 template<math_val (*F)(math_val,math_val,math_val)>
421 156 math_val fixed_arity(const std::vector<math_val>& args) {
422 156 assert_argc(args, 3);
423
4/8
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
✗ Branch 7 not taken.
186 return F(args[0], args[1], args[2]);
424 }
425
426 std::unordered_map<string, math_user_function> user_functions;
427 const std::unordered_map<string, math_builtin_function> builtin_functions = {
428 { "sqrt", fn_unary_real<sqrt> },
429 { "sin", fn_unary_real<sin> },
430 { "cos", fn_unary_real<cos> },
431 { "tan", fn_unary_real<tan> },
432 { "asin", fn_unary_real<asin> },
433 { "acos", fn_unary_real<acos> },
434 { "atan", fn_unary_real<atan> },
435 { "arcsin", fn_unary_real<asin> },
436 { "arccos", fn_unary_real<acos> },
437 { "arctan", fn_unary_real<atan> },
438 { "log", fn_unary_real<log> },
439 { "log10", fn_unary_real<log10> },
440 { "log2", fn_unary_real<log2> },
441
442 { "ceil", fn_rounding<ceil> },
443 { "floor", fn_rounding<floor> },
444
445 { "read1", fn_read<1> }, //This handles the safe and unsafe variant
446 { "read2", fn_read<2> },
447 { "read3", fn_read<3> },
448 { "read4", fn_read<4> },
449 { "canread", fn_canread<0> },
450 { "canread1", fn_canread<1> },
451 { "canread2", fn_canread<2> },
452 { "canread3", fn_canread<3> },
453 { "canread4", fn_canread<4> },
454
455 { "readfile1", fn_readfile<1> },
456 { "readfile2", fn_readfile<2> },
457 { "readfile3", fn_readfile<3> },
458 { "readfile4", fn_readfile<4> },
459 { "canreadfile", fn_canreadfile<0> },
460 { "canreadfile1", fn_canreadfile<1> },
461 { "canreadfile2", fn_canreadfile<2> },
462 { "canreadfile3", fn_canreadfile<3> },
463 { "canreadfile4", fn_canreadfile<4> },
464
465 { "filesize", fixed_arity<fn_filesize> },
466 { "getfilestatus", fixed_arity<fn_filestatus> },
467
468 { "defined", fixed_arity<fn_isdefined> },
469
470 { "snestopc", fixed_arity<fn_snes_pc<snestopc>> },
471 { "pctosnes", fixed_arity<fn_snes_pc<pctosnes>> },
472 { "realbase", { fixed_arity<fn_pc_realbase<realsnespos>>, 3 } },
473 { "pc", { fixed_arity<fn_pc_realbase<snespos>>, 3 } },
474
475 { "max", fixed_arity<fn_max> },
476 { "min", fixed_arity<fn_min> },
477 { "clamp", fixed_arity<fn_clamp> },
478
479 { "safediv", fixed_arity<fn_safediv> },
480
481 { "select", fixed_arity<fn_select> },
482 { "bank", fixed_arity<fn_bank> },
483 { "not", fixed_arity<fn_not> },
484 { "equal", math_binop_function<math_binop_type::comp_eq> },
485 { "notequal", math_binop_function<math_binop_type::comp_ne> },
486 { "less", math_binop_function<math_binop_type::comp_lt> },
487 { "lessequal", math_binop_function<math_binop_type::comp_le> },
488 { "greater", math_binop_function<math_binop_type::comp_gt> },
489 { "greaterequal", math_binop_function<math_binop_type::comp_ge> },
490
491 { "and", math_bool_binop_function<fn_and> },
492 { "or", math_bool_binop_function<fn_or> },
493 { "nand", math_bool_binop_function<fn_nand> },
494 { "nor", math_bool_binop_function<fn_nor> },
495 { "xor", math_bool_binop_function<fn_xor> },
496
497 { "round", fixed_arity<fn_round> },
498
499 { "sizeof", fixed_arity<fn_structsize_wrapper> },
500 { "objectsize", fixed_arity<fn_objectsize_wrapper> },
501 { "datasize", { fixed_arity<fn_datasize_wrapper>, 3 } },
502
503 { "stringsequal", fixed_arity<fn_str_eq<strcmp>> },
504 { "stringsequalnocase", fixed_arity<fn_str_eq<stricmp>> },
505 { "char", fixed_arity<fn_char> },
506 { "stringlength", fixed_arity<fn_strlen> },
507 };
508
509 // we don't need this struct to be exported
510 namespace {
511 // data necessary for parsing an expression, which might be an user function declaration
512 54 struct parse_context {
513 const char*& str;
514 // this map is empty unless declaring a function,
515 // in which case it maps argument name to argument index
516 const std::unordered_map<string, size_t> function_arg_names;
517 // these are methods to allow easier access to `str`
518 std::unique_ptr<math_ast_node> parse_atom();
519 std::unique_ptr<math_ast_node> parse_unops();
520 std::unique_ptr<math_ast_node> parse_binops(int depth = 0);
521 std::unique_ptr<math_ast_node> parse();
522 };
523 }
524
525
526 static const long hextable[] = {
527 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
528 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
529 -1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,
530 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
531 -1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
532 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
533 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
534 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
535 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
536 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
537 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
538 };
539
540 // "atom" = literal, parenthesized expression, or label reference
541 53494 std::unique_ptr<math_ast_node> parse_context::parse_atom() {
542
2/2
✓ Branch 0 taken 13897 times.
✓ Branch 1 taken 39597 times.
53494 if(*str == '$') {
543
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13897 times.
13897 if (!is_xdigit(*++str)) asar_throw_error(2, error_type_block, error_id_invalid_hex_value);
544 13897 int64_t ret = 0; // todo error on overflow?
545
2/2
✓ Branch 0 taken 47670 times.
✓ Branch 1 taken 13897 times.
61567 while (hextable[0 + *str] >= 0) {
546 47670 ret = (ret << 4) | hextable[0 + *str++];
547 }
548
1/2
✓ Branch 0 taken 6994 times.
✗ Branch 1 not taken.
13897 return std::make_unique<math_ast_literal>(ret);
549 }
550
8/8
✓ Branch 0 taken 35219 times.
✓ Branch 1 taken 4378 times.
✓ Branch 2 taken 17102 times.
✓ Branch 3 taken 528 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 17093 times.
✓ Branch 6 taken 2728 times.
✓ Branch 7 taken 17093 times.
39597 if (is_ualpha(*str) || *str=='.' || *str=='?') {
551 5452 const char * start=str;
552
6/6
✓ Branch 0 taken 20794 times.
✓ Branch 1 taken 20806 times.
✓ Branch 2 taken 3468 times.
✓ Branch 3 taken 5452 times.
✓ Branch 4 taken 18082 times.
✓ Branch 5 taken 2728 times.
41600 while (is_ualnum(*str) || *str == '.') str++;
553 5452 int len=(int)(str-start);
554
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 5452 times.
5578 while (*str==' ') str++;
555
2/2
✓ Branch 0 taken 2484 times.
✓ Branch 1 taken 2968 times.
5452 if (*str=='(') {
556
1/2
✓ Branch 0 taken 1242 times.
✗ Branch 1 not taken.
2484 str++;
557 1242 string func_name;
558
1/2
✓ Branch 0 taken 2484 times.
✗ Branch 1 not taken.
2484 func_name.assign(start, len);
559 1242 std::vector<std::unique_ptr<math_ast_node>> arguments;
560
2/2
✓ Branch 0 taken 3042 times.
✓ Branch 1 taken 2484 times.
6768 while(*str != ')') {
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3042 times.
3042 while (*str==' ') str++;
562
2/4
✓ Branch 0 taken 3042 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3042 times.
✗ Branch 3 not taken.
3042 arguments.emplace_back(parse_binops());
563 // is "invalid number" good here?
564
3/4
✓ Branch 0 taken 1197 times.
✓ Branch 1 taken 1845 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1197 times.
3042 if(*str != ',' && *str != ')') asar_throw_error(2, error_type_block, error_id_invalid_number);
565
2/2
✓ Branch 0 taken 648 times.
✓ Branch 1 taken 2394 times.
3042 if(*str == ',') str++;
566 }
567 2484 str++;
568
1/2
✓ Branch 0 taken 2484 times.
✗ Branch 1 not taken.
2484 return std::make_unique<math_ast_function_call>(std::move(arguments), std::move(func_name));
569 2484 } else {
570
1/2
✓ Branch 0 taken 1486 times.
✗ Branch 1 not taken.
2968 string name_part(start, len);
571
2/2
✓ Branch 0 taken 1050 times.
✓ Branch 1 taken 1918 times.
2968 if(name_part == "...") {
572 // a tiny bit ugly, but whatever
573 // ...TODO also not having a constructor for identifier is ugly too
574
2/4
✓ Branch 0 taken 525 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 525 times.
✗ Branch 3 not taken.
1050 math_val value = name_part;
575 1050 value.m_type = math_val_type::identifier;
576
1/2
✓ Branch 0 taken 1050 times.
✗ Branch 1 not taken.
1050 return std::make_unique<math_ast_literal>(value);
577 525 }
578
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 1756 times.
1918 if(!function_arg_names.empty()) {
579
1/2
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
81 auto it = function_arg_names.find(name_part);
580
1/2
✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
162 if(it != function_arg_names.end()) {
581
1/2
✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
162 return std::make_unique<math_ast_function_argument>(it->second);
582 }
583 }
584
1/2
✓ Branch 0 taken 1756 times.
✗ Branch 1 not taken.
1756 string name = labelname(&start);
585 1756 str = start;
586
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 1654 times.
1756 if(*str == '[') {
587 // struct array indexing
588 102 str++;
589
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 auto index = parse_binops();
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if(*str != ']') asar_throw_error(2, error_type_block, error_id_invalid_label_missing_closer);
591
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
102 str++;
592
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 string subname = name;
593
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 6 times.
102 if(*str == '.') {
594 // this part used to be in labelname... not sure where it really belongs....
595
6/6
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 288 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 96 times.
✓ Branch 4 taken 240 times.
✓ Branch 5 taken 48 times.
576 while (is_ualnum(*str) || *str == '.') {
596
1/2
✓ Branch 0 taken 480 times.
✗ Branch 1 not taken.
480 subname += *(str++);
597 }
598 }
599 // when doing base[index].sub:
600 // result = (base_addr + index*object_size(base)) + sub_offset
601 // = sub_addr + index*object_size(base)
602 // so we build a math node that represents this calculation
603
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 auto node_sub = std::make_unique<math_ast_label>(subname);
604
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 auto node_base = std::make_unique<math_ast_label>(name);
605 51 std::vector<unique_ptr<math_ast_node>> arg_list;
606
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 arg_list.emplace_back(std::move(node_base));
607
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 auto node_objsize = std::make_unique<math_ast_function_call>(std::move(arg_list), "objectsize");
608
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 auto node_mul = std::make_unique<math_ast_binop>(std::move(node_objsize), std::move(index), math_binop_type::mul);
609
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 auto node_add = std::make_unique<math_ast_binop>(std::move(node_mul), std::move(node_sub), math_binop_type::add);
610 51 return node_add;
611 102 } else {
612
1/2
✓ Branch 0 taken 1654 times.
✗ Branch 1 not taken.
1654 return std::make_unique<math_ast_label>(name);
613 }
614 2968 }
615 }
616
2/2
✓ Branch 0 taken 219 times.
✓ Branch 1 taken 16874 times.
17093 if(*str == '(') {
617 438 str++;
618
1/2
✓ Branch 0 taken 219 times.
✗ Branch 1 not taken.
438 auto res = parse_binops();
619
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 if(*str != ')') asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
620 438 str++;
621 219 return res;
622 438 }
623
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 16862 times.
16874 if(*str == '%') {
624
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
24 if (str[1] != '0' && str[1] != '1') asar_throw_error(2, error_type_block, error_id_invalid_binary_value);
625 24 uint64_t res = strtoull(str+1, const_cast<char**>(&str), 2);
626
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 return std::make_unique<math_ast_literal>((int64_t)res);
627 }
628
2/2
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 16532 times.
16862 if (*str=='\'') {
629
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 660 times.
660 if (!str[1]) asar_throw_error(2, error_type_block, error_id_invalid_character);
630 int orig_val;
631 660 str++;
632
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
660 str += utf8_val(&orig_val, str);
633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 660 times.
660 if (orig_val == -1) asar_throw_error(0, error_type_block, error_id_invalid_utf8);
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 660 times.
660 if (*str != '\'') asar_throw_error(2, error_type_block, error_id_invalid_character);
635
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
660 int64_t rval=thetable.get_val(orig_val);
636
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 660 times.
660 if (rval == -1)
637 {
638 // RPG Hacker: Should be fine to not check return value of codepoint_to_utf8() here, because
639 // our error cases above already made sure that orig_val contains valid data at this point.
640 string u8_str;
641 codepoint_to_utf8(&u8_str, orig_val);
642 asar_throw_error(2, error_type_block, error_id_undefined_char, u8_str.data());
643 }
644 660 str++;
645
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
660 return std::make_unique<math_ast_literal>(rval);
646 }
647
2/2
✓ Branch 0 taken 32561 times.
✓ Branch 1 taken 462 times.
33023 if (is_digit(*str)) {
648 16301 const char* end = str;
649 16301 bool is_float = false;
650
6/6
✓ Branch 0 taken 37240 times.
✓ Branch 1 taken 37257 times.
✓ Branch 2 taken 156 times.
✓ Branch 3 taken 32561 times.
✓ Branch 4 taken 20997 times.
✓ Branch 5 taken 16301 times.
74497 while (is_digit(*end) || *end == '.') {
651
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 41780 times.
41936 if(*end == '.') is_float = true;
652 41936 end++;
653 }
654 16301 string number;
655
1/2
✓ Branch 0 taken 32561 times.
✗ Branch 1 not taken.
32561 number.assign(str, (int)(end - str));
656 32561 str = end;
657
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 32405 times.
32561 if(is_float) {
658 156 double res = std::atof(number);
659
1/2
✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
156 return std::make_unique<math_ast_literal>(res);
660 } else {
661 32405 int64_t res = strtoll(number, nullptr, 10);
662
1/2
✓ Branch 0 taken 32405 times.
✗ Branch 1 not taken.
32405 return std::make_unique<math_ast_literal>(res);
663 }
664 32561 }
665
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 30 times.
462 if(*str == '"') {
666 432 const char * strpos = str + 1;
667
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 216 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
216 while (*str!='"' && *str!='\0') str++;
668 432 str = strchr(str + 1, '"'); // TODO don't we have string escapes????
669
1/2
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
432 string tempname(strpos , (int)(str - strpos));
670 432 str++;
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 432 times.
432 while (*str==' ') str++; //eat space
672
1/2
✓ Branch 0 taken 432 times.
✗ Branch 1 not taken.
432 return std::make_unique<math_ast_literal>(tempname);
673 432 }
674 30 asar_throw_error(2, error_type_block, error_id_invalid_number);
675 }
676
677 53590 std::unique_ptr<math_ast_node> parse_context::parse_unops() {
678
2/2
✓ Branch 0 taken 7092 times.
✓ Branch 1 taken 53590 times.
60682 while(*str == ' ') str++;
679 // optimize for the common case
680 // TODO how much of an optimization is this really?
681
2/2
✓ Branch 0 taken 13897 times.
✓ Branch 1 taken 39693 times.
53590 if(*str == '$') return parse_atom();
682
683
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 39621 times.
39693 if(*str == '-') {
684 72 str++;
685
2/4
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
72 return std::make_unique<math_ast_unop>(parse_unops(), math_unop_type::neg);
686
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 39609 times.
39621 } else if(*str == '~') {
687 12 str++;
688
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 return std::make_unique<math_ast_unop>(parse_unops(), math_unop_type::bit_not);
689
4/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 39567 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 30 times.
39609 } else if(*str == '<' && str[1] == ':') {
690 12 str += 2;
691
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 return std::make_unique<math_ast_unop>(parse_unops(), math_unop_type::bank_extract);
692
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39597 times.
39597 } else if(*str == '+') {
693 str++;
694 return parse_unops();
695 }
696 39597 else return parse_atom();
697 }
698
699 53580 std::unique_ptr<math_ast_node> parse_context::parse_binops(int depth) {
700 53580 const char* posneglabel = str;
701
1/2
✓ Branch 0 taken 26859 times.
✗ Branch 1 not taken.
53580 string posnegname = posneglabelname(&posneglabel, false);
702
4/4
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 53422 times.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 26815 times.
53660 if (posnegname.length() > 0 &&
703
3/4
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
158 (*posneglabel == '\0' || *posneglabel == ')')) {
704 86 str = posneglabel;
705
1/2
✓ Branch 0 taken 86 times.
✗ Branch 1 not taken.
86 return std::make_unique<math_ast_label>(posnegname);
706 }
707
708
1/2
✓ Branch 0 taken 26815 times.
✗ Branch 1 not taken.
26815 recurseblock rec;
709
710
2/2
✓ Branch 0 taken 53464 times.
✓ Branch 1 taken 30 times.
53494 std::unique_ptr<math_ast_node> left = parse_unops();
711 53464 std::unique_ptr<math_ast_node> right;
712
2/2
✓ Branch 0 taken 7092 times.
✓ Branch 1 taken 53464 times.
60556 while(*str == ' ') str++;
713
8/8
✓ Branch 0 taken 17152 times.
✓ Branch 1 taken 51130 times.
✓ Branch 2 taken 15394 times.
✓ Branch 3 taken 1758 times.
✓ Branch 4 taken 15019 times.
✓ Branch 5 taken 375 times.
✓ Branch 6 taken 7486 times.
✓ Branch 7 taken 51 times.
68282 while (*str && *str != ')' && *str != ','&& *str != ']') {
714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14968 times.
14968 while(*str == ' ') str++;
715 // TODO can we make this macro a bit nicer???
716 #define oper(name, thisdepth, contents) \
717 if (!strncmp(str, name, strlen(name))) \
718 { \
719 if (depth<=thisdepth) \
720 { \
721 str += strlen(name); \
722 right = parse_binops(thisdepth+1); \
723 left = std::make_unique<math_ast_binop>(std::move(left), std::move(right), contents); \
724 continue; \
725 } \
726 else return left; \
727 }
728
5/8
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 14932 times.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 36 times.
✗ Branch 7 not taken.
14986 oper("**", 6, math_binop_type::pow);
729
5/8
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 14824 times.
✓ Branch 2 taken 108 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 108 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 108 times.
✗ Branch 7 not taken.
14986 oper("*", 5, math_binop_type::mul);
730
5/8
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14800 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
✗ Branch 7 not taken.
14836 oper("/", 5, math_binop_type::div);
731
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 14800 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.
14800 oper("%", 5, math_binop_type::mod);
732
6/8
✓ Branch 0 taken 6040 times.
✓ Branch 1 taken 8760 times.
✓ Branch 2 taken 5980 times.
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 5980 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5980 times.
✗ Branch 7 not taken.
17788 oper("+", 4, math_binop_type::add);
733
5/8
✓ Branch 0 taken 1578 times.
✓ Branch 1 taken 7182 times.
✓ Branch 2 taken 1578 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1578 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1578 times.
✗ Branch 7 not taken.
9549 oper("-", 4, math_binop_type::sub);
734
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 7182 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.
7182 oper("<<", 3, math_binop_type::shift_left);
735
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 7182 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.
7182 oper(">>", 3, math_binop_type::shift_right);
736
737 //these two needed checked early to avoid bitwise from eating a operator
738
6/8
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 7110 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 36 times.
✗ Branch 7 not taken.
7200 oper("&&", 0, math_binop_type::logical_and);
739
6/8
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 7074 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
7119 oper("||", 0, math_binop_type::logical_or);
740
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 7074 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.
7074 oper("&", 2, math_binop_type::bit_and);
741
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 7074 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.
7074 oper("|", 2,math_binop_type::bit_or);
742
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 7074 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.
7074 oper("^", 2, math_binop_type::bit_xor);
743
744
5/8
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 7056 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
7083 oper(">=", 1, math_binop_type::comp_ge);
745
5/8
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 7038 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
7065 oper("<=", 1, math_binop_type::comp_le);
746
5/8
✓ Branch 0 taken 414 times.
✓ Branch 1 taken 6624 times.
✓ Branch 2 taken 414 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 414 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 414 times.
✗ Branch 7 not taken.
7245 oper(">", 1, math_binop_type::comp_gt);
747
5/8
✓ Branch 0 taken 5400 times.
✓ Branch 1 taken 1224 times.
✓ Branch 2 taken 5400 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5400 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5400 times.
✗ Branch 7 not taken.
9324 oper("<", 1, math_binop_type::comp_lt);
748
5/8
✓ Branch 0 taken 1134 times.
✓ Branch 1 taken 90 times.
✓ Branch 2 taken 1134 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1134 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1134 times.
✗ Branch 7 not taken.
1791 oper("==", 1, math_binop_type::comp_eq);
749
5/8
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 54 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 54 times.
✗ Branch 7 not taken.
117 oper("!=", 1, math_binop_type::comp_ne);
750 36 asar_throw_error(2, error_type_block, error_id_unknown_operator);
751 #undef oper
752 }
753 26725 return left;
754 53718 }
755
756 35180 std::unique_ptr<math_ast_node> parse_context::parse() {
757 35180 auto res = parse_binops();
758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35114 times.
35114 if(*str) {
759 if(*str == ',') asar_throw_error(2, error_type_block, error_id_invalid_input);
760 else asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
761 }
762 35114 return res;
763 }
764
765 108 void createuserfunc(const char * name, const char * arguments, const char * content) {
766
10/22
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 108 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 108 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 54 times.
✓ Branch 7 taken 54 times.
✓ Branch 8 taken 54 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 54 times.
✓ Branch 12 taken 54 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 54 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 54 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
270 if(user_functions.count(name) != 0 || builtin_functions.count(name) != 0) {
767 asar_throw_error(0, error_type_block, error_id_function_redefined, name);
768 }
769
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 string arguments_buf = arguments;
770 // TODO: if we want to be more lenient with spaces in the `function`
771 // command, then we need to handle spaces inside `arguments_buf`
772 int numargs;
773
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 autoptr<char**> spl = split(arguments_buf.raw(), ',', &numargs);
774 108 size_t arg_count = numargs;
775
8/12
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 54 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 27 times.
✓ Branch 7 taken 27 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 54 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
135 if(numargs == 1 && spl[0] == string{""}) {
776 arg_count = 0;
777 }
778 54 std::unordered_map<string, size_t> arg_indices;
779
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 108 times.
270 for(size_t i = 0; i < arg_count; i++) {
780
1/2
✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
162 string argname = spl[i];
781
3/4
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
162 if(arg_indices.count(argname)) {
782 asar_throw_error(0, error_type_block, error_id_duplicate_param_name, argname.data(), name);
783 }
784
2/4
✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 162 times.
162 if(!confirmname(argname)) {
785 asar_throw_error(0, error_type_block, error_id_invalid_param_name, argname.data());
786 }
787
1/2
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
81 arg_indices.emplace(std::move(argname), i);
788 162 }
789
790
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 parse_context ctx{ content, arg_indices };
791
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 auto parsed = ctx.parse();
792
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
108 math_user_function userfunc = { std::move(parsed), arg_count };
793
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 user_functions.emplace(name, std::move(userfunc));
794 162 }
795
796 35072 double math(const char * str)
797 {
798 35072 parse_context parse_ctx { str, {}};
799
2/2
✓ Branch 0 taken 35006 times.
✓ Branch 1 taken 66 times.
35072 std::unique_ptr<math_ast_node> parsed = parse_ctx.parse();
800
1/2
✓ Branch 0 taken 35006 times.
✗ Branch 1 not taken.
35006 int haslabel = parsed->has_label();
801 35006 foundlabel = haslabel > 0;
802 35006 foundlabel_static = haslabel < 2;
803
2/2
✓ Branch 0 taken 17421 times.
✓ Branch 1 taken 15 times.
35006 forwardlabel=false; // TODO
804 17570 math_eval_context ctx;
805
2/2
✓ Branch 0 taken 34974 times.
✓ Branch 1 taken 32 times.
35006 math_val rval = parsed->evaluate(ctx);
806
1/2
✓ Branch 0 taken 34974 times.
✗ Branch 1 not taken.
69948 return rval.get_double();
807 35073 }
808
809 29067 int64_t getnum(const char* instr)
810 {
811 29067 double num = math(instr);
812
2/2
✓ Branch 0 taken 14421 times.
✓ Branch 1 taken 14550 times.
28971 if(num < (double)INT64_MIN) {
813 return INT64_MIN;
814
2/2
✓ Branch 0 taken 14421 times.
✓ Branch 1 taken 14550 times.
28971 } else if(num > (double)INT64_MAX) {
815 return INT64_MAX;
816 }
817 28971 return (int64_t)num;
818 }
819
820 // RPG Hacker: Same function as above, but doesn't truncate our number via int conversion
821 5820 double getnumdouble(const char * instr)
822 {
823 5820 return math(instr);
824 }
825
826 2432 void initmathcore()
827 {
828 1265 user_functions.clear();
829 2432 }
830
831 2402 void deinitmathcore()
832 {
833 //not needed
834 2402 }
835