asar coverage - build #265


src/asar/
File: src/asar/math_ast.h
Date: 2025-02-28 06:59:41
Lines:
215/250
86.0%
Functions:
58/59
98.3%
Branches:
184/289
63.7%

Line Branch Exec Source
1 #include <memory>
2 #include <variant>
3 #include <vector>
4 #include "libstr.h"
5 #include "asar.h" // todo assembleblock.h sux
6 #include "assembleblock.h"
7 #include "errors.h"
8 using std::unique_ptr;
9
10 enum class math_val_type {
11 floating,
12 integer,
13 string,
14 // a resolved label with a name and value. used for datasize() and whatnot
15 identifier,
16 };
17
18 30 inline int64_t float_to_int(double f) {
19 // TODO: throw error on overflow?
20 30 return (int64_t)f;
21 }
22
23 class math_val {
24 public:
25 math_val_type m_type;
26 union {
27 double double_;
28 int64_t int_;
29 } m_numeric_val;
30 // slightly hacky but this is used for both labelname in case of
31 // m_type=identifier, and string value in case of m_type=string
32 string m_string_val;
33
34 math_val() {
35 m_type = math_val_type::integer;
36 m_numeric_val.int_ = 0;
37 }
38 302 math_val(double v) {
39 302 m_type = math_val_type::floating;
40 302 m_numeric_val.double_ = v;
41 302 }
42 24451 math_val(int64_t v) {
43 24451 m_type = math_val_type::integer;
44 24451 m_numeric_val.int_ = v;
45 24451 }
46 1154 math_val(string v) {
47 1154 m_type = math_val_type::string;
48
1/2
✓ Branch 0 taken 1154 times.
✗ Branch 1 not taken.
1154 m_string_val = v;
49 1154 }
50
51 // TODO: make all these conversions print the current type aswell instead of just expected type
52 6022 double get_double() const {
53
3/5
✓ Branch 0 taken 302 times.
✓ Branch 1 taken 11385 times.
✓ Branch 2 taken 501 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12188 switch(m_type) {
54 302 case math_val_type::floating:
55 302 return m_numeric_val.double_;
56 11385 case math_val_type::integer:
57 11385 return (double)m_numeric_val.int_;
58 501 case math_val_type::identifier:
59 501 return (double)get_integer();
60 case math_val_type::string:
61 asar_throw_error(2, error_type_block, error_id_expected_number);
62 }
63 }
64
65 5551 int64_t get_integer() const {
66
2/5
✗ Branch 0 not taken.
✓ Branch 1 taken 10505 times.
✓ Branch 2 taken 606 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
11111 switch(m_type) {
67 case math_val_type::floating:
68 return float_to_int(m_numeric_val.double_);
69 10505 case math_val_type::integer:
70 10505 return m_numeric_val.int_;
71 606 case math_val_type::identifier:
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 606 times.
606 if(!labels.exists(m_string_val))
73 // i'm not sure if it's even possible to reach here, anything
74 // that constructs identifiers should already check for existence...
75 asar_throw_error(2, error_type_block, error_id_label_not_found, m_string_val.data());
76 606 return labels.find(m_string_val).pos;
77 case math_val_type::string:
78 asar_throw_error(2, error_type_block, error_id_expected_number);
79 }
80 }
81 142 const string& get_str() const {
82
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 if(m_type == math_val_type::string) return m_string_val;
83 asar_throw_error(2, error_type_block, error_id_expected_string);
84 }
85 406 const string& get_identifier() const {
86
1/2
✓ Branch 0 taken 406 times.
✗ Branch 1 not taken.
406 if(m_type == math_val_type::identifier) return m_string_val;
87 asar_throw_error(2, error_type_block, error_id_expected_ident);
88 }
89
90 80 bool get_bool() const {
91
2/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
160 switch(m_type) {
92 4 case math_val_type::floating:
93 4 return get_double() != 0.0;
94 156 case math_val_type::integer:
95 case math_val_type::identifier:
96 156 return get_integer() != 0;
97 case math_val_type::string:
98 return get_str().length() != 0;
99 }
100 }
101 };
102
103 // any info that's necessary during evaluation
104 class math_eval_context {
105 public:
106 // TODO would it be faster to make this a reference? does that avoid any significant copies?
107 std::vector<math_val> userfunc_params;
108 };
109
110 class math_ast_node {
111 public:
112 virtual math_val evaluate(const math_eval_context&) const = 0;
113 // 0 - no label, 1 - static label, 3 - nonstatic label.
114 // (maybe tracking forwardlabel too would be good?)
115 virtual int has_label() const = 0;
116 // how many bytes long should the result of this expression be?
117 virtual int get_len(bool could_be_bank_ex) const = 0;
118 40616 virtual ~math_ast_node() = default;
119 };
120
121 using owned_node = unique_ptr<math_ast_node>;
122
123 enum class math_binop_type {
124 pow, // **
125 mul, // *
126 div, // /
127 mod, // %
128 add, // +
129 sub, // -
130 shift_left, // <<
131 shift_right, // >>
132 bit_and, // &
133 bit_or, // |
134 bit_xor, // ^
135 logical_and, // &&
136 logical_or, // ||
137 comp_ge, // >=
138 comp_le, // <=
139 comp_gt, // >
140 comp_lt, // <
141 comp_eq, // ==
142 comp_ne, // !=
143 };
144
145 template<typename T>
146 5126 T evaluate_binop_arithmetic(T lhs, T rhs, math_binop_type type) {
147
3/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 1971 times.
✓ Branch 2 taken 526 times.
✗ Branch 3 not taken.
5126 switch(type) {
148 132 case math_binop_type::mul: return lhs * rhs;
149 3942 case math_binop_type::add: return lhs + rhs;
150 1052 case math_binop_type::sub: return lhs - rhs;
151 default:
152 asar_throw_error(2, error_type_block, error_id_internal_error, "evaluate_binop_arithmetic with bad type");
153 }
154 }
155 template<typename T>
156 5060 bool evaluate_binop_compare(T lhs, T rhs, math_binop_type type) {
157
6/7
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 154 times.
✓ Branch 3 taken 1816 times.
✓ Branch 4 taken 496 times.
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
5060 switch(type) {
158 24 case math_binop_type::comp_ge: return lhs >= rhs;
159 24 case math_binop_type::comp_le: return lhs <= rhs;
160 308 case math_binop_type::comp_gt: return lhs > rhs;
161 3632 case math_binop_type::comp_lt: return lhs < rhs;
162 992 case math_binop_type::comp_eq: return lhs == rhs;
163 80 case math_binop_type::comp_ne: return lhs != rhs;
164 default:
165 asar_throw_error(2, error_type_block, error_id_internal_error, "evaluate_binop_compare with bad type");
166 }
167 }
168
169 2569 static math_val evaluate_binop(math_val lhs, math_val rhs, math_binop_type type) {
170 // todo: do this a bit smarter (bit_ops shouldn't cast int->float->int)
171
3/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 5079 times.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
5141 if(lhs.m_type == math_val_type::floating) rhs = math_val(rhs.get_double());
172
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5067 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
5079 else if(rhs.m_type == math_val_type::floating) lhs = math_val(lhs.get_double());
173
7/12
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 2563 times.
✓ Branch 10 taken 2530 times.
✗ Branch 11 not taken.
5141 switch(type) {
174 12 case math_binop_type::pow: return math_val(pow(lhs.get_double(), rhs.get_double()));
175 16 case math_binop_type::div:
176
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
16 if(rhs.get_double() == 0.0)
177 4 asar_throw_error(2, error_type_block, error_id_division_by_zero);
178 12 return math_val(lhs.get_double() / rhs.get_double());
179 case math_binop_type::mod:
180 if(rhs.get_double() == 0.0)
181 asar_throw_error(2, error_type_block, error_id_division_by_zero);
182 // TODO: negative semantics
183 if(lhs.m_type == math_val_type::floating) {
184 return math_val(fmod(lhs.get_double(), rhs.get_double()));
185 } else {
186 return math_val(lhs.get_integer() % rhs.get_integer());
187 }
188 break;
189
190 6 case math_binop_type::shift_left:
191 {
192 6 int64_t rhs_v = rhs.get_integer();
193
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(rhs_v < 0)
194 6 asar_throw_error(2, error_type_block, error_id_negative_shift);
195 return math_val(lhs.get_integer() << (uint64_t)rhs_v);
196 }
197 12 case math_binop_type::shift_right:
198 {
199 12 int64_t rhs_v = rhs.get_integer();
200
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if(rhs_v < 0)
201 6 asar_throw_error(2, error_type_block, error_id_negative_shift);
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
6 return math_val(lhs.get_integer() >> (uint64_t)rhs_v);
203 }
204
205 case math_binop_type::bit_and: return math_val(lhs.get_integer() & rhs.get_integer());
206 2 case math_binop_type::bit_or: return math_val(lhs.get_integer() | rhs.get_integer());
207 case math_binop_type::bit_xor: return math_val(lhs.get_integer() ^ rhs.get_integer());
208
209 case math_binop_type::logical_and:
210 case math_binop_type::logical_or:
211 asar_throw_error(2, error_type_block, error_id_internal_error, "evaluate_binop() on logical ops loses short-circuiting");
212
213 2563 case math_binop_type::mul:
214 case math_binop_type::add:
215 case math_binop_type::sub:
216 // TODO error on string (also TODO support string +)
217
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2537 times.
2563 if(lhs.m_type == math_val_type::floating)
218 26 return math_val(evaluate_binop_arithmetic<double>(lhs.get_double(), rhs.get_double(), type));
219 else
220 2537 return math_val(evaluate_binop_arithmetic<int64_t>(lhs.get_integer(), rhs.get_integer(), type));
221
222 2530 case math_binop_type::comp_ge:
223 case math_binop_type::comp_le:
224 case math_binop_type::comp_gt:
225 case math_binop_type::comp_lt:
226 case math_binop_type::comp_eq:
227 case math_binop_type::comp_ne:
228
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 2482 times.
2530 if(lhs.m_type == math_val_type::floating)
229 48 return math_val((int64_t)evaluate_binop_compare<double>(lhs.get_double(), rhs.get_double(), type));
230 else
231 2482 return math_val((int64_t)evaluate_binop_compare<int64_t>(lhs.get_integer(), rhs.get_integer(), type));
232 }
233 }
234
235 class math_ast_binop : public math_ast_node {
236 public:
237 owned_node m_left, m_right;
238 math_binop_type m_type;
239 5228 math_ast_binop(owned_node left_in, owned_node right_in, math_binop_type type_in)
240 5228 : m_left(std::move(left_in)), m_right(std::move(right_in)), m_type(type_in) {}
241
242
243 5118 math_val evaluate(const math_eval_context& ctx) const {
244
1/2
✓ Branch 0 taken 5118 times.
✗ Branch 1 not taken.
5118 math_val lhs = m_left->evaluate(ctx);
245
246 // handle short-circuiting for || and &&
247
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5106 times.
5118 if(m_type == math_binop_type::logical_or) {
248
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
12 if(lhs.get_bool() == true) return math_val((int64_t)true);
249
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 math_val rhs = m_right->evaluate(ctx);
250
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 return math_val((int64_t)rhs.get_bool());
251 6 }
252
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 5088 times.
5106 if(m_type == math_binop_type::logical_and) {
253
3/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 12 times.
18 if(lhs.get_bool() == false) return math_val((int64_t)false);
254
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 math_val rhs = m_right->evaluate(ctx);
255
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 return math_val((int64_t)rhs.get_bool());
256 12 }
257
258
2/2
✓ Branch 0 taken 5087 times.
✓ Branch 1 taken 1 times.
5088 math_val rhs = m_right->evaluate(ctx);
259
4/6
✓ Branch 0 taken 5087 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5087 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5071 times.
✓ Branch 5 taken 16 times.
5127 return evaluate_binop(lhs, rhs, m_type);
260 5134 }
261
262 5130 int has_label() const {
263 5130 return m_left->has_label() | m_right->has_label();
264 }
265
266 int get_len(bool could_be_bank_ex) const;
267 };
268
269 enum class math_unop_type {
270 neg,
271 bit_not,
272 bank_extract,
273 };
274
275 class math_ast_unop : public math_ast_node {
276 public:
277 owned_node m_arg;
278 math_unop_type m_type;
279 96 math_ast_unop(owned_node arg_in, math_unop_type type_in)
280 96 : m_arg(std::move(arg_in)), m_type(type_in) {}
281 35 math_val evaluate(const math_eval_context& ctx) const {
282
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 math_val arg = m_arg->evaluate(ctx);
283
2/4
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
70 switch(m_type) {
284 64 case math_unop_type::neg:
285
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
64 if(arg.m_type == math_val_type::floating) return math_val(-arg.get_double());
286
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 else return math_val(-arg.get_integer());
287 case math_unop_type::bit_not: return math_val(~arg.get_integer());
288
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 case math_unop_type::bank_extract: return math_val(arg.get_integer() >> 16);
289 }
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 }
291 70 int has_label() const {
292 70 return m_arg->has_label();
293 }
294 18 int get_len(bool could_be_bank_ex) const {
295
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if(could_be_bank_ex && m_type == math_unop_type::bank_extract)
296 18 return 1;
297 return m_arg->get_len(false);
298 }
299 };
300
301 class math_ast_literal : public math_ast_node {
302 math_val m_value;
303 int m_len;
304 public:
305
1/2
✓ Branch 0 taken 19128 times.
✗ Branch 1 not taken.
19128 math_ast_literal(math_val value, int len=0) : m_value(value), m_len(len) {}
306 16588 math_val evaluate(const math_eval_context& ctx) const { return m_value; }
307 16612 int has_label() const { return 0; }
308 2466 int get_len(bool could_be_bank_ex) const { return m_len; }
309 friend int math_ast_binop::get_len(bool) const;
310 };
311
312 class math_ast_label : public math_ast_node {
313 string m_labelname;
314 // current namespace when this label was referenced
315 string m_cur_ns;
316 public:
317 // this should be the output of labelname() already
318 1458 math_ast_label(string labelname)
319
1/2
✓ Branch 0 taken 735 times.
✗ Branch 1 not taken.
2916 : m_labelname(labelname)
320 // this is initialized with the global ns
321
2/4
✓ Branch 0 taken 1458 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 723 times.
✗ Branch 3 not taken.
2193 , m_cur_ns(ns) {}
322 666 math_val evaluate(const math_eval_context& ctx) const {
323
12/18
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 640 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 20 times.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 26 times.
✓ Branch 9 taken 640 times.
✓ Branch 10 taken 23 times.
✓ Branch 11 taken 643 times.
✓ Branch 12 taken 10 times.
✓ Branch 13 taken 326 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
666 if(m_cur_ns && labels.exists(m_cur_ns + m_labelname)) {
324
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 math_val v = math_val(m_cur_ns + m_labelname);
325 20 v.m_type = math_val_type::identifier;
326 20 return v;
327 20 }
328
2/2
✓ Branch 0 taken 642 times.
✓ Branch 1 taken 4 times.
646 else if(labels.exists(m_labelname)) {
329
2/4
✓ Branch 0 taken 642 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 642 times.
✗ Branch 3 not taken.
642 math_val v = math_val(m_labelname);
330 642 v.m_type = math_val_type::identifier;
331 642 return v;
332 642 }
333 else {
334 // i think in this context we always should throw???
335 4 asar_throw_error(2, error_type_block, error_id_label_not_found, m_labelname.data());
336 }
337 }
338 666 int has_label() const {
339
12/18
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 640 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 20 times.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 26 times.
✓ Branch 9 taken 640 times.
✓ Branch 10 taken 23 times.
✓ Branch 11 taken 643 times.
✓ Branch 12 taken 10 times.
✓ Branch 13 taken 326 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
666 if(m_cur_ns && labels.exists(m_cur_ns + m_labelname)) {
340
4/8
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 10 times.
20 return labels.find(m_cur_ns + m_labelname).is_static ? 1 : 3;
341 }
342
2/2
✓ Branch 0 taken 642 times.
✓ Branch 1 taken 4 times.
646 else if(labels.exists(m_labelname)) {
343
4/4
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 558 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 238 times.
642 return labels.find(m_labelname).is_static ? 1 : 3;
344 }
345 // otherwise, non-static label
346 4 return 3;
347 }
348
349 738 int get_len(bool could_be_bank_ex) const {
350 738 snes_label label;
351
12/18
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 708 times.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 20 times.
✓ Branch 8 taken 30 times.
✓ Branch 9 taken 708 times.
✓ Branch 10 taken 20 times.
✓ Branch 11 taken 718 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 367 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
738 if(m_cur_ns && labels.exists(m_cur_ns + m_labelname)) {
352
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 label = labels.find(m_cur_ns + m_labelname);
353 }
354
3/4
✓ Branch 0 taken 728 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 629 times.
✓ Branch 3 taken 99 times.
728 else if(labels.exists(m_labelname)) {
355
1/2
✓ Branch 0 taken 629 times.
✗ Branch 1 not taken.
629 label = labels.find(m_labelname);
356 }
357 99 else return 2;
358
1/2
✓ Branch 0 taken 639 times.
✗ Branch 1 not taken.
639 return getlenforlabel(label, true);
359 }
360 };
361
362 class math_builtin_function {
363 using callable_t = math_val(*)(const std::vector<math_val>& args);
364 callable_t inner;
365 int m_has_label;
366 public:
367 bool m_is_bank;
368 462 math_builtin_function(callable_t c, int l = 0, bool is_bank = false)
369 462 : inner(c), m_has_label(l), m_is_bank(is_bank) {}
370 954 math_val call(const std::vector<math_val>& args) const {
371 954 return inner(args);
372 }
373 954 int has_label() const {
374 954 return m_has_label;
375 }
376 };
377
378 class math_user_function {
379 int m_arg_count;
380 owned_node m_func_body;
381 public:
382 36 math_user_function(owned_node body, size_t arg_count)
383 36 : m_arg_count(arg_count), m_func_body(std::move(body)) {}
384 22 math_val call(const std::vector<math_val>& args) const {
385 22 math_eval_context new_ctx;
386
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 new_ctx.userfunc_params = args;
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if(args.size() != m_arg_count)
388 asar_throw_error(2, error_type_block, error_id_argument_count, m_arg_count, (int)args.size());
389
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
44 return m_func_body->evaluate(new_ctx);
390 22 }
391 22 int has_label() const {
392 22 return m_func_body->has_label();
393 }
394 };
395
396 class math_function_ref {
397 public:
398 std::variant<const math_builtin_function*, const math_user_function*> inner;
399 1034 math_function_ref(const math_builtin_function& fn) : inner(&fn) {}
400 22 math_function_ref(const math_user_function& fn) : inner(&fn) {}
401 976 math_val call(const std::vector<math_val>& args) const {
402
2/2
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 52 times.
3852 return std::visit([&](auto& i) { return i->call(args); }, inner);
403 }
404 976 int has_label() const {
405
1/2
✓ Branch 0 taken 976 times.
✗ Branch 1 not taken.
2928 return std::visit([&](auto& i) { return i->has_label(); }, inner);
406 }
407 };
408
409 extern std::unordered_map<string, math_user_function> user_functions;
410 extern const std::unordered_map<string, math_builtin_function> builtin_functions;
411
412 class math_ast_function_call : public math_ast_node {
413 std::vector<owned_node> m_arguments;
414 math_function_ref m_func;
415 1056 static math_function_ref lookup_fname(string const& function_name) {
416
3/4
✓ Branch 0 taken 1056 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 1034 times.
1056 if(auto it = user_functions.find(function_name); it != user_functions.end()) {
417 22 return it->second;
418
2/4
✓ Branch 0 taken 1034 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1034 times.
✗ Branch 3 not taken.
1034 } else if(auto it = builtin_functions.find(function_name); it != builtin_functions.end()) {
419 1034 return it->second;
420 } else {
421 asar_throw_error(2, error_type_block, error_id_function_not_found, function_name.data());
422 }
423 }
424
425 public:
426 1056 math_ast_function_call(std::vector<owned_node> args, string function_name)
427 1587 : m_arguments(std::move(args))
428
1/2
✓ Branch 0 taken 1056 times.
✗ Branch 1 not taken.
2112 , m_func(lookup_fname(function_name)) {}
429 976 math_val evaluate(const math_eval_context& ctx) const {
430 976 std::vector<math_val> arg_vals;
431
2/2
✓ Branch 0 taken 1250 times.
✓ Branch 1 taken 976 times.
2226 for(auto const& p : m_arguments) {
432
2/4
✓ Branch 0 taken 1250 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1250 times.
✗ Branch 3 not taken.
1250 arg_vals.push_back(p->evaluate(ctx));
433 }
434
2/2
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 52 times.
1900 return m_func.call(arg_vals);
435 976 }
436 976 int has_label() const {
437 976 int out = m_func.has_label();
438
2/2
✓ Branch 0 taken 1250 times.
✓ Branch 1 taken 976 times.
2226 for(auto const& p : m_arguments) {
439
1/2
✓ Branch 0 taken 1250 times.
✗ Branch 1 not taken.
1250 out |= p->has_label();
440 }
441 976 return out;
442 }
443 78 int get_len(bool could_be_bank_ex) const {
444
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 36 times.
78 if(could_be_bank_ex) {
445 42 auto v = std::get_if<const math_builtin_function*>(&m_func.inner);
446
5/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 15 times.
42 if(v && (*v)->m_is_bank) return 1;
447 }
448 // have nothing to go on, good default i guess
449 // TODO for realbase this might be wrong? bc that might be in another bank?
450
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if(m_arguments.size() == 0) return 2;
451 66 int res = 0;
452
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 66 times.
132 for(auto& v : m_arguments) {
453
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 res = std::max(res, v->get_len(false));
454 }
455 66 return res;
456 }
457 };
458
459 // only for use inside user function definitions
460 class math_ast_function_argument : public math_ast_node {
461 size_t m_arg_idx;
462 public:
463 54 math_ast_function_argument(size_t arg_idx) : m_arg_idx(arg_idx) {}
464
465 34 math_val evaluate(const math_eval_context& ctx) const {
466 34 return ctx.userfunc_params[m_arg_idx];
467 }
468
469 // if a function is called with a label as an argument, that gets checked by
470 // the function call node, not here
471 34 int has_label() const { return 0; }
472 // i don't think these should ever have their len gotten?
473 int get_len(bool could_be_bank_ex) const { return 0; }
474 };
475
476 84 inline int math_ast_binop::get_len(bool could_be_bank_ex) const {
477
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 54 times.
84 if(could_be_bank_ex) {
478 30 int want_rhs = 0;
479
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
30 if(m_type == math_binop_type::div) {
480 12 want_rhs = 65536;
481
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 } else if(m_type == math_binop_type::shift_right) {
482 12 want_rhs = 16;
483 }
484
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 6 times.
30 if(want_rhs) {
485 24 math_ast_node* right_ptr = m_right.get();
486
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 auto right_lit = dynamic_cast<math_ast_literal*>(right_ptr);
487
2/4
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 if(right_lit && right_lit->m_value.m_type == math_val_type::integer) {
488 24 int64_t right_val = right_lit->m_value.get_integer();
489
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if(right_val == want_rhs) return 1;
490 }
491 }
492 }
493
2/4
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
60 return std::max(m_left->get_len(false), m_right->get_len(false));
494 }
495