The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
function.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#include "formula/function.hpp"
17
18#include "color.hpp"
20#include "formula/debugger.hpp"
21#include "game_config.hpp"
22#include "game_display.hpp"
23#include "log.hpp"
24#include "pathutils.hpp"
26
27#include <boost/algorithm/string.hpp>
28#include <boost/math/constants/constants.hpp>
29#include <cctype>
30#include <chrono>
31#include <deque>
32#include <utility>
33
34using namespace boost::math::constants;
35
36static lg::log_domain log_engine("engine");
37#define DBG_NG LOG_STREAM(debug, log_engine)
38static lg::log_domain log_scripting_formula("scripting/formula");
39#define LOG_SF LOG_STREAM(info, log_scripting_formula)
40#define WRN_SF LOG_STREAM(warn, log_scripting_formula)
41#define ERR_SF LOG_STREAM(err, log_scripting_formula)
42
43namespace wfl
44{
45/**
46 * For printing error messages when WFL parsing or evaluation fails, this contains the names of the WFL functions being evaluated.
47 *
48 * Two C++ threads might be evaluating WFL at the same; declaring this thread_local is a quick bugfix which should probably be replaced
49 * by having a context-object for each WFL evaluation.
50 */
51thread_local static std::deque<std::string> call_stack;
52
54{
55 call_stack.push_back(str);
56}
57
62
64{
65 std::ostringstream res;
66 for(const auto& frame : call_stack) {
67 if(!frame.empty()) {
68 res << " " << frame << "\n";
69 }
70 }
71
72 return res.str();
73}
74
75std::string function_expression::str() const
76{
77 std::stringstream s;
78 s << get_name();
79 s << '(';
80 bool first_arg = true;
81 for(expression_ptr a : args()) {
82 if(!first_arg) {
83 s << ',';
84 } else {
85 first_arg = false;
86 }
87 s << a->str();
88 }
89 s << ')';
90 return s.str();
91}
92
93namespace builtins
94{
96{
97 std::shared_ptr<formula_debugger> fdbp;
98 bool need_wrapper = false;
99
100 if(fdb == nullptr) {
101 fdbp.reset(new formula_debugger());
102 fdb = fdbp.get();
103 need_wrapper = true;
104 }
105
106 if(args().size() == 1) {
107 if(!need_wrapper) {
108 return args()[0]->evaluate(variables, fdb);
109 } else {
110 return wrapper_formula(args()[0]).evaluate(variables, fdb);
111 }
112 }
113
114 return wrapper_formula().evaluate(variables, fdb);
115}
116
118{
119 variant var = args()[0]->evaluate(variables, fdb);
120
121 auto callable = var.as_callable();
122 formula_input_vector inputs = callable->inputs();
123
124 std::vector<variant> res;
125 for(std::size_t i = 0; i < inputs.size(); ++i) {
126 const formula_input& input = inputs[i];
127 res.emplace_back(input.name);
128 }
129
130 return variant(std::move(res));
131}
132
134{
135 for(std::size_t n = 0; n < args().size() - 1; n += 2) {
136 if(args()[n]->evaluate(variables, fdb).as_bool()) {
137 return args()[n + 1]->evaluate(variables, fdb);
138 }
139 }
140
141 if((args().size() % 2) != 0) {
142 return args().back()->evaluate(variables, fdb);
143 } else {
144 return variant();
145 }
146}
147
149{
150 variant var = args()[0]->evaluate(variables, fdb);
151
152 for(std::size_t n = 1; n < args().size() - 1; n += 2) {
153 variant val = args()[n]->evaluate(variables, fdb);
154
155 if(val == var) {
156 return args()[n + 1]->evaluate(variables, fdb);
157 }
158 }
159
160 if((args().size() % 2) == 0) {
161 return args().back()->evaluate(variables, fdb);
162 } else {
163 return variant();
164 }
165}
166
168{
169 const variant input = args()[0]->evaluate(variables, fdb);
170 if(input.is_decimal()) {
171 const int n = input.as_decimal();
172 return variant(n >= 0 ? n : -n, variant::DECIMAL_VARIANT);
173 } else {
174 const int n = input.as_int();
175 return variant(n >= 0 ? n : -n);
176 }
177}
178
180{
181 variant res = args()[0]->evaluate(variables, fdb);
182 if(res.is_list()) {
183 if(res.is_empty()) {
184 throw formula_error("min(list): list is empty", "", "", 0);
185 }
186
187 res = *std::min_element(res.begin(), res.end());
188 }
189
190 for(std::size_t n = 1; n < args().size(); ++n) {
191 variant v = args()[n]->evaluate(variables, fdb);
192
193 if(v.is_list()) {
194 if(v.is_empty()) {
195 continue;
196 }
197
198 v = *std::min_element(v.begin(), v.end());
199 }
200
201 if(res.is_null() || v < res) {
202 res = v;
203 }
204 }
205
206 return res;
207}
208
210{
211 variant res = args()[0]->evaluate(variables, fdb);
212 if(res.is_list()) {
213 if(res.is_empty()) {
214 throw formula_error("max(list): list is empty", "", "", 0);
215 }
216
217 res = *std::max_element(res.begin(), res.end());
218 }
219
220 for(std::size_t n = 1; n < args().size(); ++n) {
221 variant v = args()[n]->evaluate(variables, fdb);
222
223 if(v.is_list()) {
224 if(v.is_empty()) {
225 continue;
226 }
227
228 v = *std::max_element(v.begin(), v.end());
229 }
230
231 if(res.is_null() || v > res) {
232 res = v;
233 }
234 }
235
236 return res;
237}
238
239namespace
240{
241void display_float(const map_location& location, const std::string& text)
242{
243 if(auto gd = game_display::get_singleton()) {
244 gd->float_label(location, text, color_t(255, 0, 0));
245 }
246}
247} // end anon namespace
248
249DEFINE_WFL_FUNCTION(debug_float, 2, 3)
250{
251 const args_list& arguments = args();
252 const variant var0 = arguments[0]->evaluate(variables, fdb);
253 variant var1 = arguments[1]->evaluate(variables, fdb);
254
255 const map_location location = callable_cast<location_callable&>(var0).loc();
256 std::string text;
257
258 if(arguments.size() == 2) {
259 text = var1.to_debug_string();
260 display_float(location, text);
261 return var1;
262 } else {
263 const variant var2 = arguments[2]->evaluate(variables, fdb);
264 text = var1.string_cast() + var2.to_debug_string();
265 display_float(location, text);
266 return var2;
267 }
268}
269
270DEFINE_WFL_FUNCTION(debug_print, 1, 2)
271{
272 std::string speaker = "WFL";
273 int i_value = 0;
274
275 if(args().size() == 2) {
276 speaker = args()[0]->evaluate(variables, fdb).string_cast();
277 i_value = 1;
278 }
279
280 variant value = args()[i_value]->evaluate(variables, fdb);
281 const std::string str = value.to_debug_string(true);
282
283 LOG_SF << speaker << ": " << str;
284
287 std::chrono::system_clock::now(), speaker, 0, str, events::chat_handler::MESSAGE_PUBLIC, false);
288 }
289
290 return value;
291}
292
293DEFINE_WFL_FUNCTION(debug_profile, 1, 2)
294{
295 std::string speaker = "WFL";
296 int i_value = 0;
297
298 if(args().size() == 2) {
299 speaker = args()[0]->evaluate(variables, fdb).string_cast();
300 i_value = 1;
301 }
302
303 const variant value = args()[i_value]->evaluate(variables, fdb);
304 const int run_count = 1000;
305 std::chrono::steady_clock::duration run_time;
306
307 for(int i = 0; i < run_count; i++) {
308 const auto start = std::chrono::steady_clock::now();
309 args()[i_value]->evaluate(variables, fdb);
310 run_time += std::chrono::steady_clock::now() - start;
311 }
312
313 // Average execution time over all runs
314 auto average_ms = std::chrono::duration_cast<std::chrono::milliseconds>(run_time / run_count);
315
316 std::ostringstream str;
317#ifdef __cpp_lib_format
318 str << "Evaluated in " << average_ms << " on average";
319#else
320 str << "Evaluated in " << average_ms.count() << " ms on average";
321#endif
322
323 LOG_SF << speaker << ": " << str.str();
324
327 std::chrono::system_clock::now(), speaker, 0, str.str(), events::chat_handler::MESSAGE_PUBLIC, false);
328 }
329
330 return value;
331}
332
334{
335 const variant map = args()[0]->evaluate(variables, fdb);
336 return map.get_keys();
337}
338
340{
341 const variant map = args()[0]->evaluate(variables, fdb);
342 return map.get_values();
343}
344
346{
347 const variant var = args()[0]->evaluate(variables, fdb);
348
349 std::vector<variant> tmp;
350 for(variant_iterator it = var.begin(); it != var.end(); ++it) {
351 tmp.push_back(*it);
352 }
353
354 return variant(std::move(tmp));
355}
356
358{
359 const variant var_1 = args()[0]->evaluate(variables, fdb);
360
361 std::map<variant, variant> tmp;
362
363 if(args().size() == 2) {
364 const variant var_2 = args()[1]->evaluate(variables, fdb);
365 if(var_1.num_elements() != var_2.num_elements()) {
366 return variant();
367 }
368
369 for(std::size_t i = 0; i < var_1.num_elements(); ++i) {
370 tmp[var_1[i]] = var_2[i];
371 }
372 } else {
373 for(variant_iterator it = var_1.begin(); it != var_1.end(); ++it) {
374 if(auto kv = callable_cast<key_value_pair*>(*it)) {
375 tmp[kv->query_value("key")] = kv->query_value("value");
376 } else {
377 auto map_it = tmp.find(*it);
378
379 if(map_it == tmp.end()) {
380 tmp[*it] = variant(1);
381 } else {
382 map_it->second = variant(map_it->second.as_int() + 1);
383 }
384 }
385 }
386 }
387
388 return variant(std::move(tmp));
389}
390
391DEFINE_WFL_FUNCTION(substring, 2, 3)
392{
393 std::string result = args()[0]->evaluate(variables, fdb).as_string();
394
395 int offset = args()[1]->evaluate(variables, fdb).as_int();
396 if(offset < 0) {
397 offset += result.size();
398
399 if(offset < 0) {
400 offset = 0;
401 }
402 } else {
403 if(static_cast<std::size_t>(offset) >= result.size()) {
404 return variant(std::string());
405 }
406 }
407
408 if(args().size() > 2) {
409 int size = args()[2]->evaluate(variables, fdb).as_int();
410
411 if(size < 0) {
412 size = -size;
413 offset = std::max(0, offset - size + 1);
414 }
415
416 return variant(result.substr(offset, size));
417 }
418
419 return variant(result.substr(offset));
420}
421
423{
424 std::string result = args()[0]->evaluate(variables, fdb).as_string();
425 std::string replacement = args().back()->evaluate(variables, fdb).as_string();
426
427 int offset = args()[1]->evaluate(variables, fdb).as_int();
428 if(offset < 0) {
429 offset += result.size();
430
431 if(offset < 0) {
432 offset = 0;
433 }
434 } else {
435 if(static_cast<std::size_t>(offset) >= result.size()) {
436 return variant(result);
437 }
438 }
439
440 if(args().size() > 3) {
441 int size = args()[2]->evaluate(variables, fdb).as_int();
442
443 if(size < 0) {
444 size = -size;
445 offset = std::max(0, offset - size + 1);
446 }
447
448 return variant(result.replace(offset, size, replacement));
449 }
450
451 return variant(result.replace(offset, std::string::npos, replacement));
452}
453
454DEFINE_WFL_FUNCTION(replace_all, 3, 3)
455{
456 std::string result = args()[0]->evaluate(variables, fdb).as_string();
457 std::string needle = args()[1]->evaluate(variables, fdb).as_string();
458 std::string replacement = args().back()->evaluate(variables, fdb).as_string();
459 boost::replace_all(result, needle, replacement);
460 return variant(result);
461}
462
463DEFINE_WFL_FUNCTION(starts_with, 2, 2)
464{
465 std::string str = args()[0]->evaluate(variables, fdb).as_string();
466 std::string prefix = args()[1]->evaluate(variables, fdb).as_string();
467 return variant(boost::starts_with(str, prefix));
468}
469
470DEFINE_WFL_FUNCTION(ends_with, 2, 2)
471{
472 std::string str = args()[0]->evaluate(variables, fdb).as_string();
473 std::string prefix = args()[1]->evaluate(variables, fdb).as_string();
474 return variant(boost::ends_with(str, prefix));
475}
476
478{
479 std::string result = args()[0]->evaluate(variables, fdb).as_string();
480 std::string insert = args().back()->evaluate(variables, fdb).as_string();
481
482 int offset = args()[1]->evaluate(variables, fdb).as_int();
483 if(offset < 0) {
484 offset += result.size();
485
486 if(offset < 0) {
487 offset = 0;
488 }
489 } else if(static_cast<std::size_t>(offset) >= result.size()) {
490 return variant(result + insert);
491 }
492
493 return variant(result.insert(offset, insert));
494}
495
497{
498 return variant(args()[0]->evaluate(variables, fdb).as_string().length());
499}
500
501DEFINE_WFL_FUNCTION(byte_index, 2, 2)
502{
503 return variant(utf8::index(
504 args()[0]->evaluate(variables, fdb).as_string(),
505 args()[1]->evaluate(variables, fdb).as_int()));
506}
507
508DEFINE_WFL_FUNCTION(concatenate, 1, -1)
509{
510 std::string result;
511 for(expression_ptr arg : args()) {
512 result += arg->evaluate(variables, fdb).string_cast();
513 }
514
515 return variant(result);
516}
517
518DEFINE_WFL_FUNCTION(str_upper, 1, 1)
519{
520 std::string str = args()[0]->evaluate(variables, fdb).as_string();
521 std::transform(str.begin(), str.end(), str.begin(), static_cast<int (*)(int)>(std::toupper));
522 return variant(str);
523}
524
525DEFINE_WFL_FUNCTION(str_lower, 1, 1)
526{
527 std::string str = args()[0]->evaluate(variables, fdb).as_string();
528 std::transform(str.begin(), str.end(), str.begin(), static_cast<int (*)(int)>(std::tolower));
529 return variant(str);
530}
531
533{
534 const double angle = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
535 const double result = std::sin(angle * pi<double>() / 180.0);
536 return variant(result, variant::DECIMAL_VARIANT);
537}
538
540{
541 const double angle = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
542 const double result = std::cos(angle * pi<double>() / 180.0);
543 return variant(result, variant::DECIMAL_VARIANT);
544}
545
547{
548 const double angle = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
549 const double result = std::tan(angle * pi<double>() / 180.0);
550 if(std::isnan(result) || result <= std::numeric_limits<int>::min() || result >= std::numeric_limits<int>::max()) {
551 return variant();
552 }
553
554 return variant(result, variant::DECIMAL_VARIANT);
555}
556
558{
559 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
560 const double result = std::asin(num) * 180.0 / pi<double>();
561 if(std::isnan(result)) {
562 return variant();
563 }
564
565 return variant(result, variant::DECIMAL_VARIANT);
566}
567
569{
570 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
571 const double result = std::acos(num) * 180.0 / pi<double>();
572 if(std::isnan(result)) {
573 return variant();
574 }
575
576 return variant(result, variant::DECIMAL_VARIANT);
577}
578
580{
581 if(args().size() == 1) {
582 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
583 const double result = std::atan(num) * 180.0 / pi<double>();
584 return variant(result, variant::DECIMAL_VARIANT);
585 } else {
586 const double y = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
587 const double x = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
588 const double result = std::atan2(y, x) * 180.0 / pi<double>();
589 return variant(result, variant::DECIMAL_VARIANT);
590 }
591}
592
594{
595 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
596 const double result = std::sqrt(num);
597 if(std::isnan(result)) {
598 return variant();
599 }
600
601 return variant(result, variant::DECIMAL_VARIANT);
602}
603
605{
606 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
607 const double result = num < 0 ? -std::pow(-num, 1.0 / 3.0) : std::pow(num, 1.0 / 3.0);
608 return variant(result, variant::DECIMAL_VARIANT);
609}
610
612{
613 const double base = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
614 const double root = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
615 const double result = base < 0 && std::fmod(root, 2) == 1 ? -std::pow(-base, 1.0 / root) : std::pow(base, 1.0 / root);
616 if(std::isnan(result)) {
617 return variant();
618 }
619
620 return variant(result, variant::DECIMAL_VARIANT);
621}
622
624{
625 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
626 if(args().size() == 1) {
627 const double result = std::log(num);
628 if(std::isnan(result)) {
629 return variant();
630 }
631
632 return variant(result, variant::DECIMAL_VARIANT);
633 }
634
635 const double base = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
636 const double result = std::log(num) / std::log(base);
637 if(std::isnan(result)) {
638 return variant();
639 }
640
641 return variant(result, variant::DECIMAL_VARIANT);
642}
643
645{
646 const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
647 const double result = std::exp(num);
648 if(result == 0 || result >= std::numeric_limits<int>::max()) {
649 // These are range errors rather than NaNs,
650 // but I figure it's better than returning INT_MIN.
651 return variant();
652 }
653
654 return variant(result, variant::DECIMAL_VARIANT);
655}
656
658{
659 return variant(pi<double>(), variant::DECIMAL_VARIANT);
660}
661
663{
664 const double x = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
665 const double y = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
666 return variant(std::hypot(x, y), variant::DECIMAL_VARIANT);
667}
668
669DEFINE_WFL_FUNCTION(index_of, 2, 2)
670{
671 const variant value = args()[0]->evaluate(variables, fdb);
672 const variant list = args()[1]->evaluate(variables, fdb);
673
674 for(std::size_t i = 0; i < list.num_elements(); ++i) {
675 if(list[i] == value) {
676 return variant(i);
677 }
678 }
679
680 return variant(-1);
681}
682
684{
685 const variant items = args()[0]->evaluate(variables, fdb);
686 variant max_value;
688
689 if(args().size() == 2) {
690 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
691 const variant val = args().back()->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
692
693 if(max == variant_iterator() || val > max_value) {
694 max = it;
695 max_value = val;
696 }
697 }
698 } else {
699 map_formula_callable self_callable;
700 const std::string self = args()[1]->evaluate(variables, fdb).as_string();
701
702 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
703 self_callable.add(self, *it);
704
705 const variant val = args().back()->evaluate(
706 formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
707
708 if(max == variant_iterator() || val > max_value) {
709 max = it;
710 max_value = val;
711 }
712 }
713 }
714
715 if(max == variant_iterator()) {
716 return variant();
717 }
718
719 return *max;
720}
721
723{
724 const int value = args()[0]->evaluate(variables, fdb).as_int() % 1000;
725 const double angle = 2.0 * pi<double>() * (static_cast<double>(value) / 1000.0);
726 return variant(static_cast<int>(std::sin(angle) * 1000.0));
727}
728
730{
731 const double lo = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "lerp:lo")).as_decimal() / 1000.0;
732 const double hi = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "lerp:hi")).as_decimal() / 1000.0;;
733 const double alpha = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "lerp:alpha")).as_decimal() / 1000.0;;
734 return variant(static_cast<int>((lo + alpha * (hi - lo)) * 1000.0), variant::DECIMAL_VARIANT);
735}
736
737DEFINE_WFL_FUNCTION(lerp_index, 2, 2)
738{
739 const std::vector<variant> items = args()[0]->evaluate(variables, fdb).as_list();
740 if(items.empty()) return variant();
741 const double alpha = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
742 // Same formula as red_to_green etc
743 const double val_scaled = std::clamp(0.01 * alpha, 0.0, 1.0);
744 const int idx = int(std::nearbyint((items.size() - 1) * val_scaled));
745 return items[idx];
746}
747
748DEFINE_WFL_FUNCTION(get_palette, 1, 1)
749{
750 const std::string name = args()[0]->evaluate(variables, fdb).as_string();
751 std::vector<color_t> colors;
752 if(name == "red_green_scale") {
754 } else if(name == "red_green_scale_text") {
756 } else if(name == "blue_white_scale") {
758 } else if(name == "blue_white_scale_text") {
760 } else {
761 colors = game_config::tc_info(name);
762 }
763 std::vector<variant> result;
764 result.reserve(colors.size());
765 for(auto clr : colors) {
766 result.emplace_back(std::make_shared<color_callable>(clr));
767 }
768 return variant(std::move(result));
769}
770
772{
773 const variant val = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "clamp:value"));
774 const variant lo = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "clamp:lo"));
775 const variant hi = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "clamp:hi"));
776 if(val.is_int() && lo.is_int() && hi.is_int()) {
777 return variant(std::clamp<int>(val.as_int(), lo.as_int(), hi.as_int()));
778 }
779 return variant(static_cast<int>(std::clamp<int>(val.as_decimal(), lo.as_decimal(), hi.as_decimal())), variant::DECIMAL_VARIANT);
780}
781
782namespace
783{
784class variant_comparator : public formula_callable
785{
786public:
787 variant_comparator(const expression_ptr& expr, const formula_callable& fallback)
788 : expr_(expr)
789 , fallback_(&fallback)
790 , a_()
791 , b_()
792 {
793 }
794
795 bool operator()(const variant& a, const variant& b) const
796 {
797 a_ = a;
798 b_ = b;
799 return expr_->evaluate(*this).as_bool();
800 }
801
802private:
803 variant get_value(const std::string& key) const
804 {
805 if(key == "a") {
806 return a_;
807 } else if(key == "b") {
808 return b_;
809 } else {
810 return fallback_->query_value(key);
811 }
812 }
813
814 void get_inputs(formula_input_vector& inputs) const
815 {
816 fallback_->get_inputs(inputs);
817 }
818
820 const formula_callable* fallback_;
821 mutable variant a_, b_;
822};
823} // end anon namespace
824
826{
827 variant list = args()[0]->evaluate(variables, fdb);
828
829 std::vector<variant> vars;
830 vars.reserve(list.num_elements());
831
832 for(std::size_t n = 0; n != list.num_elements(); ++n) {
833 vars.push_back(list[n]);
834 }
835
836 if(args().size() == 1) {
837 std::sort(vars.begin(), vars.end());
838 } else {
839 std::sort(vars.begin(), vars.end(), variant_comparator(args()[1], variables));
840 }
841
842 return variant(std::move(vars));
843}
844
846{
847 const variant& arg = args()[0]->evaluate(variables, fdb);
848
849 if(arg.is_string()) {
850 std::string str = args()[0]->evaluate(variables, fdb).as_string();
851 std::reverse(str.begin(), str.end());
852
853 return variant(std::move(str));
854 } else if(arg.is_list()) {
855 std::vector<variant> list = args()[0]->evaluate(variables, fdb).as_list();
856 std::reverse(list.begin(), list.end());
857
858 return variant(std::move(list));
859 }
860
861 return variant();
862}
863
864DEFINE_WFL_FUNCTION(contains_string, 2, 2)
865{
866 std::string str = args()[0]->evaluate(variables, fdb).as_string();
867 std::string key = args()[1]->evaluate(variables, fdb).as_string();
868
869 return variant(str.find(key) != std::string::npos);
870}
871
872DEFINE_WFL_FUNCTION(find_string, 2, 2)
873{
874 const std::string str = args()[0]->evaluate(variables, fdb).as_string();
875 const std::string key = args()[1]->evaluate(variables, fdb).as_string();
876
877 std::size_t pos = str.find(key);
878 return variant(static_cast<int>(pos));
879}
880
882{
883 std::vector<variant> list_vars;
884 std::map<variant, variant> map_vars;
885
886 const variant items = args()[0]->evaluate(variables, fdb);
887
888 if(args().size() == 2) {
889 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
890 const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
891
892 if(val.as_bool()) {
893 if(items.is_map()) {
894 map_vars[(*it).get_member("key")] = (*it).get_member("value");
895 } else {
896 list_vars.push_back(*it);
897 }
898 }
899 }
900 } else {
901 map_formula_callable self_callable;
902 const std::string self = args()[1]->evaluate(variables, fdb).as_string();
903
904 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
905 self_callable.add(self, *it);
906
907 const variant val = args()[2]->evaluate(
908 formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
909
910 if(val.as_bool()) {
911 if(items.is_map()) {
912 map_vars[(*it).get_member("key")] = (*it).get_member("value");
913 } else {
914 list_vars.push_back(*it);
915 }
916 }
917 }
918 }
919
920 if(items.is_map()) {
921 return variant(std::move(map_vars));
922 }
923
924 return variant(std::move(list_vars));
925}
926
928{
929 const variant items = args()[0]->evaluate(variables, fdb);
930
931 if(args().size() == 2) {
932 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
933 const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
934 if(val.as_bool()) {
935 return *it;
936 }
937 }
938 } else {
939 map_formula_callable self_callable;
940 const std::string self = args()[1]->evaluate(variables, fdb).as_string();
941
942 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
943 self_callable.add(self, *it);
944
945 const variant val = args().back()->evaluate(
946 formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
947
948 if(val.as_bool()) {
949 return *it;
950 }
951 }
952 }
953
954 return variant();
955}
956
958{
959 std::vector<variant> list_vars;
960 std::map<variant, variant> map_vars;
961 const variant items = args()[0]->evaluate(variables, fdb);
962
963 if(args().size() == 2) {
964 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
965 const variant val = args().back()->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
966 if(items.is_map()) {
967 map_vars[(*it).get_member("key")] = val;
968 } else {
969 list_vars.push_back(val);
970 }
971 }
972 } else {
973 map_formula_callable self_callable;
974 const std::string self = args()[1]->evaluate(variables, fdb).as_string();
975
976 for(variant_iterator it = items.begin(); it != items.end(); ++it) {
977 self_callable.add(self, *it);
978
979 const variant val = args().back()->evaluate(
980 formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
981
982 if(items.is_map()) {
983 map_vars[(*it).get_member("key")] = val;
984 } else {
985 list_vars.push_back(val);
986 }
987 }
988 }
989
990 if(items.is_map()) {
991 return variant(std::move(map_vars));
992 }
993
994 return variant(std::move(list_vars));
995}
996
997DEFINE_WFL_FUNCTION(take_while, 2, 2)
998{
999 const variant& items = args()[0]->evaluate(variables, fdb);
1000
1001 variant_iterator it = items.begin();
1002 for(; it != items.end(); ++it) {
1003 const variant matches = args().back()->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
1004
1005 if(!matches.as_bool()) {
1006 break;
1007 }
1008 }
1009
1010 return variant(std::vector(items.begin(), it));
1011}
1012
1013namespace
1014{
1015struct indexer
1016{
1017 explicit indexer(std::size_t i)
1018 : i(i)
1019 {
1020 }
1021
1022 variant operator()(const variant& v) const
1023 {
1024 if(i >= v.num_elements()) {
1025 return variant();
1026 } else {
1027 return v[i];
1028 }
1029 }
1030
1031 std::size_t i;
1032};
1033
1034/** @todo: replace with lambda? */
1035struct comparator
1036{
1037 bool operator()(const variant& a, const variant& b) const
1038 {
1039 return a.num_elements() < b.num_elements();
1040 }
1041};
1042
1043std::vector<variant> get_input(
1045 const formula_callable& variables,
1046 formula_debugger* fdb)
1047{
1048 if(args.size() == 1) {
1049 const variant list = args[0]->evaluate(variables, fdb);
1050 return std::vector<variant>(list.begin(), list.end());
1051 } else {
1052 std::vector<variant> input;
1053 input.reserve(args.size());
1054
1055 for(expression_ptr expr : args) {
1056 input.push_back(expr->evaluate(variables, fdb));
1057 }
1058
1059 return input;
1060 }
1061}
1062} // end anon namespace
1063
1065{
1066 const std::vector<variant> input = get_input(args(), variables, fdb);
1067 std::vector<variant> output;
1068
1069 // So basically this does [[a,b,c],[d,e,f],[x,y,z]] -> [[a,d,x],[b,e,y],[c,f,z]]
1070 // Or [[a,b,c,d],[x,y,z]] -> [[a,x],[b,y],[c,z],[d,null()]]
1071 std::size_t max_i = std::max_element(input.begin(), input.end(), comparator())->num_elements();
1072 output.reserve(max_i);
1073
1074 for(std::size_t i = 0; i < max_i; i++) {
1075 std::vector<variant> elem(input.size());
1076 std::transform(input.begin(), input.end(), elem.begin(), indexer(i));
1077 output.emplace_back(elem);
1078 }
1079
1080 return variant(std::move(output));
1081}
1082
1084{
1085 const variant items = args()[0]->evaluate(variables, fdb);
1086 variant initial = args().size() == 2 ? variant() : args()[1]->evaluate(variables, fdb);
1087
1088 if(items.num_elements() == 0) {
1089 return initial;
1090 }
1091
1092 variant_iterator it = items.begin();
1093 variant res(initial.is_null() ? *it : initial);
1094 if(res != initial) {
1095 ++it;
1096 }
1097
1098 map_formula_callable self_callable;
1099 for(; it != items.end(); ++it) {
1100 self_callable.add("a", res);
1101 self_callable.add("b", *it);
1102 res = args().back()->evaluate(
1103 formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
1104 }
1105
1106 return res;
1107}
1108
1110{
1111 variant res(0);
1112 const variant items = args()[0]->evaluate(variables, fdb);
1113 if(items.num_elements() > 0) {
1114 if(items[0].is_list()) {
1115 res = variant(std::vector<variant>{});
1116 if(args().size() >= 2) {
1117 res = args()[1]->evaluate(variables, fdb);
1118 if(!res.is_list())
1119 return variant();
1120 }
1121 } else if(items[0].is_map()) {
1122 res = variant(std::map<variant, variant>{});
1123 if(args().size() >= 2) {
1124 res = args()[1]->evaluate(variables, fdb);
1125 if(!res.is_map())
1126 return variant();
1127 }
1128 } else {
1129 if(args().size() >= 2) {
1130 res = args()[1]->evaluate(variables, fdb);
1131 }
1132 }
1133 }
1134
1135 for(std::size_t n = 0; n != items.num_elements(); ++n) {
1136 res = res + items[n];
1137 }
1138
1139 return res;
1140}
1141
1143{
1144 const variant items = args()[0]->evaluate(variables, fdb);
1145 variant_iterator it = items.begin();
1146 if(it == items.end()) {
1147 return variant();
1148 }
1149
1150 if(args().size() == 1) {
1151 return *it;
1152 }
1153
1154 const int n = items.num_elements(), req = args()[1]->evaluate(variables, fdb).as_int();
1155 const int count = req < 0 ? n - std::min(-req, n) : std::min(req, n);
1156
1157 variant_iterator end = it;
1158 std::advance(end, count);
1159
1160 std::vector<variant> res;
1161 std::copy(it, end, std::back_inserter(res));
1162 return variant(std::move(res));
1163}
1164
1166{
1167 const variant items = args()[0]->evaluate(variables, fdb);
1168 variant_iterator it = items.end();
1169 if(it == items.begin()) {
1170 return variant();
1171 }
1172
1173 if(args().size() == 1) {
1174 return *--it;
1175 }
1176
1177 const int n = items.num_elements(), req = args()[1]->evaluate(variables, fdb).as_int();
1178 const int count = req < 0 ? n - std::min(-req, n) : std::min(req, n);
1179
1180 std::advance(it, -count);
1181 std::vector<variant> res;
1182
1183 std::copy(it, items.end(), std::back_inserter(res));
1184 return variant(std::move(res));
1185}
1186
1188{
1189 const variant items = args()[0]->evaluate(variables, fdb);
1190 return variant(static_cast<int>(items.num_elements()));
1191}
1192
1194{
1195 if(!args().empty()) {
1196 for(std::size_t i = 0; i < args().size(); ++i) {
1197 args()[i]->evaluate(variables, fdb);
1198 }
1199 }
1200
1201 return variant();
1202}
1203
1205{
1206 variant decimal = args()[0]->evaluate(variables, fdb);
1207 int d = decimal.as_decimal();
1208
1209 if((d >= 0) && (d % 1000 != 0)) {
1210 d /= 1000;
1211 return variant(++d);
1212 } else {
1213 d /= 1000;
1214 return variant(d);
1215 }
1216}
1217
1219{
1220 variant decimal = args()[0]->evaluate(variables, fdb);
1221 int d = decimal.as_decimal();
1222 int f = d % 1000;
1223
1224 if(f >= 500) {
1225 d /= 1000;
1226 return variant(++d);
1227 } else if(f <= -500) {
1228 d /= 1000;
1229 return variant(--d);
1230 } else {
1231 d /= 1000;
1232 return variant(d);
1233 }
1234}
1235
1237{
1238 variant decimal = args()[0]->evaluate(variables, fdb);
1239 int d = decimal.as_decimal();
1240
1241 if((d < 0) && (d % 1000 != 0)) {
1242 d /= 1000;
1243 return variant(--d);
1244 } else {
1245 d /= 1000;
1246 return variant(d);
1247 }
1248}
1249
1251{
1252 variant decimal = args()[0]->evaluate(variables, fdb);
1253 int d = decimal.as_int();
1254
1255 return variant(d);
1256}
1257
1259{
1260 variant decimal = args()[0]->evaluate(variables, fdb);
1261 int d = decimal.as_decimal();
1262
1263 d %= 1000;
1265}
1266
1268{
1269 variant decimal = args()[0]->evaluate(variables, fdb);
1270 int d = decimal.as_decimal();
1271
1272 if(d != 0) {
1273 d = d > 0 ? 1 : -1;
1274 }
1275
1276 return variant(d);
1277}
1278
1279DEFINE_WFL_FUNCTION(as_decimal, 1, 1)
1280{
1281 variant decimal = args()[0]->evaluate(variables, fdb);
1282 int d = decimal.as_decimal();
1283
1285}
1286
1288{
1289 return make_callable<location_callable>(map_location(
1290 args()[0]->evaluate(variables, add_debug_info(fdb, 0, "loc:x")).as_int(),
1291 args()[1]->evaluate(variables, add_debug_info(fdb, 1, "loc:y")).as_int(), wml_loc()
1292 ));
1293}
1294
1296{
1297 return make_callable<key_value_pair>(
1298 args()[0]->evaluate(variables, add_debug_info(fdb, 0, "pair:key")),
1299 args()[1]->evaluate(variables, add_debug_info(fdb, 1, "pair_value"))
1300 );
1301}
1302
1304{
1305 map_location loc1, loc2;
1306 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "distance_between:location_A")))) {
1307 loc1 = loc_c->loc();
1308 }
1309
1310 if (auto loc_c = callable_cast<location_callable*>(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "distance_between:location_B")))) {
1311 loc2 = loc_c->loc();
1312 }
1313
1314 if (!loc1.valid() || !loc2.valid()) {
1315 return variant();
1316 }
1317
1318 return variant(distance_between(loc1, loc2));
1319}
1320
1321DEFINE_WFL_FUNCTION(nearest_loc, 2, 2)
1322{
1324 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location")))) {
1325 loc = loc_c->loc();
1326 }
1327
1328 if(!loc.valid()) {
1329 return variant();
1330 }
1331
1332 const std::vector<variant> locations = args()[1]
1333 ->evaluate(variables, add_debug_info(fdb, 1, "nearest_loc:locations"))
1334 .as_list();
1335
1336#ifdef __cpp_lib_ranges
1337 auto nearest = std::ranges::min_element(locations, {},
1338 [&](const variant& cmp) { return distance_between(loc, callable_cast<location_callable&>(cmp).loc()); });
1339#else
1340 auto nearest = std::min_element(locations.begin(), locations.end(), [&](const variant& cmp1, const variant& cmp2) {
1341 return std::less{}(
1342 distance_between(loc, callable_cast<location_callable&>(cmp1).loc()),
1343 distance_between(loc, callable_cast<location_callable&>(cmp2).loc())
1344 );
1345 });
1346#endif
1347
1348 if(nearest != locations.end()) {
1349 return make_callable<location_callable>(callable_cast<location_callable&>(*nearest).loc());
1350 } else {
1351 return variant();
1352 }
1353}
1354
1355DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
1356{
1358 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")))) {
1359 loc = loc_c->loc();
1360 }
1361
1362 if(!loc.valid()) {
1363 return variant();
1364 }
1365
1366 std::vector<variant> v;
1367 for(const map_location& adj : get_adjacent_tiles(loc)) {
1368 v.emplace_back(std::make_shared<location_callable>(adj));
1369 }
1370
1371 return variant(std::move(v));
1372}
1373
1374DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
1375{
1377 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, fdb))) {
1378 loc = loc_c->loc();
1379 }
1380
1381 if(!loc.valid()) {
1382 return variant();
1383 }
1384
1385 int range = args()[1]->evaluate(variables, fdb).as_int();
1386
1387 if(range < 0) {
1388 return variant();
1389 }
1390
1391 if(!range) {
1392 return make_callable<location_callable>(loc);
1393 }
1394
1395 std::vector<map_location> res;
1396
1397 get_tiles_in_radius(loc, range, res);
1398
1399 std::vector<variant> v;
1400 v.reserve(res.size() + 1);
1401 v.emplace_back(std::make_shared<location_callable>(loc));
1402
1403 for(std::size_t n = 0; n != res.size(); ++n) {
1404 v.emplace_back(std::make_shared<location_callable>(res[n]));
1405 }
1406
1407 return variant(std::move(v));
1408}
1409
1410DEFINE_WFL_FUNCTION(are_adjacent, 2, 2)
1411{
1412 map_location loc1, loc2;
1413 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "are_adjacent:location_A")))) {
1414 loc1 = loc_c->loc();
1415 }
1416
1417 if (auto loc_c = callable_cast<location_callable*>(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B")))) {
1418 loc2 = loc_c->loc();
1419 }
1420
1421 if (!loc1.valid() || !loc2.valid()) {
1422 return variant();
1423 }
1424
1425 return variant(tiles_adjacent(loc1, loc2) ? 1 : 0);
1426}
1427
1428DEFINE_WFL_FUNCTION(relative_dir, 2, 2)
1429{
1430 map_location loc1, loc2;
1431 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "relative_dir:location_A")))) {
1432 loc1 = loc_c->loc();
1433 }
1434
1435 if (auto loc_c = callable_cast<location_callable*>(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B")))) {
1436 loc2 = loc_c->loc();
1437 }
1438
1439 if (!loc1.valid() || !loc2.valid()) {
1440 return variant();
1441 }
1442
1444}
1445
1446DEFINE_WFL_FUNCTION(direction_from, 2, 3)
1447{
1449 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:location")))) {
1450 loc = loc_c->loc();
1451 }
1452
1453 const std::string dir_str =
1454 args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:dir")).as_string();
1455
1456 int n = args().size() == 3
1457 ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1458 : 1;
1459
1460 if (!loc.valid()) {
1461 return variant();
1462 }
1463 return make_callable<location_callable>(loc.get_direction(map_location::parse_direction(dir_str), n));
1464}
1465
1466DEFINE_WFL_FUNCTION(rotate_loc_around, 2, 3)
1467{
1468 map_location center, loc;
1469 if (auto loc_c = callable_cast<location_callable*>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:center")))) {
1470 center = loc_c->loc();
1471 }
1472
1473 if (auto loc_c = callable_cast<location_callable*>(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location")))) {
1474 loc = loc_c->loc();
1475 }
1476
1477 if (!center.valid() || !loc.valid()) {
1478 return variant();
1479 }
1480
1481 int n = args().size() == 3
1482 ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1483 : 1;
1484
1485 return make_callable<location_callable>(loc.rotate_right_around_center(center, n));
1486}
1487
1489{
1490 const variant& v = args()[0]->evaluate(variables, fdb);
1492}
1493
1494} // namespace builtins
1495
1496namespace actions
1497{
1498DEFINE_WFL_FUNCTION(safe_call, 2, 2)
1499{
1500 const variant main = args()[0]->evaluate(variables, fdb);
1501 const expression_ptr backup_formula = args()[1];
1502
1503 return make_callable<safe_call_callable>(main, backup_formula);
1504}
1505
1507{
1508 return make_callable<set_var_callable>(
1509 args()[0]->evaluate(variables, add_debug_info(fdb, 0, "set_var:key")).as_string(),
1510 args()[1]->evaluate(variables, add_debug_info(fdb, 1, "set_var:value")));
1511}
1512
1513} // namespace actions
1514
1515variant key_value_pair::get_value(const std::string& key) const
1516{
1517 if(key == "key") {
1518 return key_;
1519 } else if(key == "value") {
1520 return value_;
1521 }
1522
1523 return variant();
1524}
1525
1526void key_value_pair::get_inputs(formula_input_vector& inputs) const
1527{
1528 add_input(inputs, "key");
1529 add_input(inputs, "value");
1530}
1531
1532std::string key_value_pair::serialize_to_string() const
1533{
1534 std::ostringstream ss;
1535 ss << "pair(";
1536 ss << key_.serialize_to_string();
1537 ss << ",";
1538 ss << value_.serialize_to_string();
1539 ss << ")";
1540 return ss.str();
1541}
1542
1543formula_function_expression::formula_function_expression(const std::string& name,
1544 const args_list& args,
1546 const_formula_ptr precondition,
1547 const std::vector<std::string>& arg_names)
1548 : function_expression(name, args, arg_names.size(), arg_names.size())
1549 , formula_(std::move(formula))
1550 , precondition_(std::move(precondition))
1551 , arg_names_(arg_names)
1552 , star_arg_(-1)
1553{
1554 for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1555 if(arg_names_[n].empty() == false && arg_names_[n].back() == '*') {
1556 arg_names_[n].resize(arg_names_[n].size() - 1);
1557 star_arg_ = n;
1558 break;
1559 }
1560 }
1561}
1562
1564{
1565 static std::string indent;
1566 indent += " ";
1567
1568 DBG_NG << indent << "executing '" << formula_->str() << "'";
1569
1570 const auto begin_time = std::chrono::steady_clock::now();
1571 map_formula_callable callable;
1572
1573 for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1574 variant var = args()[n]->evaluate(variables, fdb);
1575 callable.add(arg_names_[n], var);
1576
1577 if(static_cast<int>(n) == star_arg_) {
1578 callable.set_fallback(var.as_callable());
1579 }
1580 }
1581
1582 if(precondition_) {
1583 if(!precondition_->evaluate(callable, fdb).as_bool()) {
1584 DBG_NG << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
1585
1586 for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1587 DBG_NG << " arg " << (n + 1) << ": " << args()[n]->evaluate(variables, fdb).to_debug_string();
1588 }
1589 }
1590 }
1591
1592 variant res = formula_->evaluate(callable, fdb);
1593
1594 const auto taken = std::chrono::steady_clock::now() - begin_time;
1595 DBG_NG << indent << "returning: " << taken.count();
1596
1597 indent.resize(indent.size() - 2);
1598
1599 return res;
1600}
1601
1603 const std::vector<expression_ptr>& args) const
1604{
1605 return std::make_shared<formula_function_expression>(name_, args, formula_, precondition_, args_);
1606}
1607
1608function_symbol_table::function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1609 : parent(parent ? parent : get_builtins())
1610{
1611}
1612
1614{
1615 custom_formulas_.insert_or_assign(name, std::move(fcn));
1616}
1617
1619 const std::string& fn, const std::vector<expression_ptr>& args) const
1620{
1621 const auto i = custom_formulas_.find(fn);
1622 if(i != custom_formulas_.end()) {
1623 return i->second->generate_function_expression(args);
1624 }
1625
1626 if(parent) {
1627 expression_ptr res(parent->create_function(fn, args));
1628 if(res) {
1629 return res;
1630 }
1631 }
1632
1633 throw formula_error("Unknown function: " + fn, "", "", 0);
1634}
1635
1637{
1638 std::set<std::string> res;
1639 if(parent) {
1640 res = parent->get_function_names();
1641 }
1642
1643 for(const auto& formula : custom_formulas_) {
1644 res.insert(formula.first);
1645 }
1646
1647 return res;
1648}
1649
1650std::shared_ptr<function_symbol_table> function_symbol_table::get_builtins()
1651{
1652 static function_symbol_table functions_table(builtins_tag);
1653
1654 if(functions_table.empty()) {
1655 functions_table.parent = nullptr;
1656
1657 using namespace builtins;
1661 DECLARE_WFL_FUNCTION(switch);
1665 DECLARE_WFL_FUNCTION(choose);
1666 DECLARE_WFL_FUNCTION(debug_float);
1667 DECLARE_WFL_FUNCTION(debug_print);
1668 DECLARE_WFL_FUNCTION(debug_profile);
1671 DECLARE_WFL_FUNCTION(contains_string);
1672 DECLARE_WFL_FUNCTION(find_string);
1673 DECLARE_WFL_FUNCTION(reverse);
1674 DECLARE_WFL_FUNCTION(filter);
1678 DECLARE_WFL_FUNCTION(take_while);
1679 DECLARE_WFL_FUNCTION(reduce);
1686 DECLARE_WFL_FUNCTION(floor);
1687 DECLARE_WFL_FUNCTION(trunc);
1690 DECLARE_WFL_FUNCTION(round);
1691 DECLARE_WFL_FUNCTION(as_decimal);
1695 DECLARE_WFL_FUNCTION(nearest_loc);
1696 DECLARE_WFL_FUNCTION(adjacent_locs);
1697 DECLARE_WFL_FUNCTION(locations_in_radius);
1698 DECLARE_WFL_FUNCTION(are_adjacent);
1699 DECLARE_WFL_FUNCTION(relative_dir);
1700 DECLARE_WFL_FUNCTION(direction_from);
1701 DECLARE_WFL_FUNCTION(rotate_loc_around);
1702 DECLARE_WFL_FUNCTION(index_of);
1704 DECLARE_WFL_FUNCTION(values);
1705 DECLARE_WFL_FUNCTION(tolist);
1706 DECLARE_WFL_FUNCTION(tomap);
1707 DECLARE_WFL_FUNCTION(substring);
1708 DECLARE_WFL_FUNCTION(replace);
1709 DECLARE_WFL_FUNCTION(replace_all);
1710 DECLARE_WFL_FUNCTION(starts_with);
1711 DECLARE_WFL_FUNCTION(ends_with);
1712 DECLARE_WFL_FUNCTION(length);
1713 DECLARE_WFL_FUNCTION(byte_index);
1714 DECLARE_WFL_FUNCTION(concatenate);
1727 DECLARE_WFL_FUNCTION(hypot);
1730 DECLARE_WFL_FUNCTION(lerp_index);
1731 DECLARE_WFL_FUNCTION(clamp);
1732 DECLARE_WFL_FUNCTION(get_palette);
1733 }
1734
1735 return std::shared_ptr<function_symbol_table>(&functions_table, [](function_symbol_table*) {});
1736}
1737
1738action_function_symbol_table::action_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1739 : function_symbol_table(parent)
1740{
1741 using namespace actions;
1742 function_symbol_table& functions_table = *this;
1743 DECLARE_WFL_FUNCTION(safe_call);
1744 DECLARE_WFL_FUNCTION(set_var);
1745}
1746}
map_location loc
Definition move.cpp:172
#define DBG_NG
#define debug(x)
std::unique_ptr< PangoAttribute, void(*)(PangoAttribute *)> value_
#define LOG_SF
void add_chat_message(const std::chrono::system_clock::time_point &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
static game_display * get_singleton()
display_chat_manager & get_chat_manager()
action_function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
std::string get_name() const
Definition function.hpp:86
variant evaluate(const formula_callable &variables, formula_debugger *fdb=nullptr) const
Definition function.hpp:75
std::vector< std::string > arg_names_
Definition function.hpp:168
variant execute(const formula_callable &variables, formula_debugger *fdb) const
const_formula_ptr precondition_
Definition function.hpp:166
virtual std::string str() const
Definition function.cpp:75
const args_list & args() const
Definition function.hpp:123
std::vector< expression_ptr > args_list
Definition function.hpp:105
static std::shared_ptr< function_symbol_table > get_builtins()
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
void add_function(const std::string &name, formula_function_ptr &&fcn)
std::set< std::string > get_function_names() const
std::shared_ptr< function_symbol_table > parent
Definition function.hpp:252
functions_map custom_formulas_
Definition function.hpp:253
map_formula_callable & add(const std::string &key, const variant &value)
Definition callable.hpp:249
void set_fallback(const_formula_callable_ptr fallback)
Definition callable.hpp:261
function_expression_ptr generate_function_expression(const std::vector< expression_ptr > &args) const
const_formula_ptr precondition_
Definition function.hpp:211
const_formula_ptr formula_
Definition function.hpp:210
std::vector< std::string > args_
Definition function.hpp:212
Iterator class for the variant.
Definition variant.hpp:160
bool is_map() const
Definition variant.hpp:75
bool is_list() const
Definition variant.hpp:73
variant get_keys() const
Definition variant.cpp:277
variant_iterator begin() const
Definition variant.cpp:297
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition variant.cpp:336
variant get_values() const
Definition variant.cpp:287
int as_decimal(int fallback=0) const
Returns the variant's internal representation of decimal number: ie, 1.234 is represented as 1234.
Definition variant.cpp:344
const_formula_callable_ptr as_callable() const
Definition variant.cpp:377
std::size_t num_elements() const
Definition variant.cpp:312
formula_variant::type type() const
Definition variant.cpp:226
bool is_decimal() const
Definition variant.hpp:71
bool is_empty() const
Definition variant.cpp:307
variant get_member(const std::string &name) const
Definition variant.cpp:321
const std::string & as_string() const
Definition variant.cpp:362
std::string string_cast() const
Definition variant.cpp:681
variant_iterator end() const
Definition variant.cpp:302
bool as_bool() const
Returns a boolean state of the variant value.
Definition variant.cpp:357
bool is_string() const
Definition variant.hpp:74
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition variant.cpp:686
bool is_null() const
Functions to test the type of the internal value.
Definition variant.hpp:69
bool is_int() const
Definition variant.hpp:70
Formula AI debugger.
variant a_
Definition function.cpp:821
const formula_callable * fallback_
Definition function.cpp:820
static lg::log_domain log_engine("engine")
static lg::log_domain log_scripting_formula("scripting/formula")
variant b_
Definition function.cpp:821
expression_ptr expr_
Definition function.cpp:819
#define DEFINE_WFL_FUNCTION(name, min_args, max_args)
Helper macro to declare an associated class for a WFL function.
Definition function.hpp:27
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition function.hpp:47
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition location.cpp:584
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition location.cpp:513
#define sgn(x)
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition location.cpp:541
static std::ostream & output()
Definition log.cpp:103
static int indent
Definition log.cpp:88
Standard logging facilities (interface).
std::vector< color_t > red_green_scale_text
const std::vector< color_t > & tc_info(std::string_view name)
const bool & debug
std::vector< color_t > blue_white_scale
std::vector< color_t > red_green_scale
std::vector< color_t > blue_white_scale_text
boost::variant< constant, n_var, boost::recursive_wrapper< not_op >, boost::recursive_wrapper< ternary_op > > expr
STL namespace.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition unicode.cpp:70
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
std::vector< formula_input > formula_input_vector
std::shared_ptr< const formula > const_formula_ptr
std::shared_ptr< formula_expression > expression_ptr
Definition formula.hpp:29
static thread_local std::deque< std::string > call_stack
For printing error messages when WFL parsing or evaluation fails, this contains the names of the WFL ...
Definition function.cpp:51
std::shared_ptr< function_expression > function_expression_ptr
Definition function.hpp:173
std::shared_ptr< formula_function > formula_function_ptr
Definition function.hpp:230
void get_tiles_in_radius(const map_location &center, const int radius, std::vector< map_location > &result)
Function that will add to result all locations within radius tiles of center (excluding center itself...
Definition pathutils.cpp:56
int main(int, char **)
Definition sdl3.cpp:25
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
The basic class for representing 8-bit RGB or RGBA colour values.
Definition color.hpp:51
Encapsulates the map of the game.
Definition location.hpp:46
static std::string write_direction(direction dir)
Definition location.cpp:154
map_location get_direction(direction dir, unsigned int n=1u) const
Definition location.cpp:362
map_location rotate_right_around_center(const map_location &center, int k) const
Definition location.cpp:299
bool valid() const
Definition location.hpp:111
direction get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition location.cpp:238
static direction parse_direction(const std::string &str)
Definition location.cpp:79
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition enum_base.hpp:46
call_stack_manager(const std::string &str)
Definition function.cpp:53
static std::string get()
Definition function.cpp:63
static map_location::direction n
static map_location::direction s
#define i
#define d
#define f
#define b