The Battle for Wesnoth  1.19.17+dev
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 
34 using namespace boost::math::constants;
35 
36 static lg::log_domain log_engine("engine");
37 #define DBG_NG LOG_STREAM(debug, log_engine)
38 static 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 
43 namespace 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  */
51 thread_local static std::deque<std::string> call_stack;
52 
53 call_stack_manager::call_stack_manager(const std::string& str)
54 {
55  call_stack.push_back(str);
56 }
57 
58 call_stack_manager::~call_stack_manager()
59 {
60  call_stack.pop_back();
61 }
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 
75 std::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 
93 namespace 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(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 
148 DEFINE_WFL_FUNCTION(switch, 3, -1)
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 
239 namespace
240 {
241 void 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 
249 DEFINE_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 = var0.convert_to<location_callable>()->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 
270 DEFINE_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 
293 DEFINE_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 
345 DEFINE_WFL_FUNCTION(tolist, 1, 1)
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(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 = (*it).try_convert<key_value_pair>()) {
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(tmp);
389 }
390 
391 DEFINE_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 
422 DEFINE_WFL_FUNCTION(replace, 3, 4)
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 
454 DEFINE_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 
463 DEFINE_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 
470 DEFINE_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 
496 DEFINE_WFL_FUNCTION(length, 1, 1)
497 {
498  return variant(args()[0]->evaluate(variables, fdb).as_string().length());
499 }
500 
501 DEFINE_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 
508 DEFINE_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 
518 DEFINE_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 
525 DEFINE_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 
669 DEFINE_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 
683 DEFINE_WFL_FUNCTION(choose, 2, 3)
684 {
685  const variant items = args()[0]->evaluate(variables, fdb);
686  variant max_value;
687  variant_iterator max;
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 
737 DEFINE_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 
748 DEFINE_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(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 
782 namespace
783 {
784 class variant_comparator : public formula_callable
785 {
786 public:
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 
802 private:
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(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(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(list);
859  }
860 
861  return variant();
862 }
863 
864 DEFINE_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 
872 DEFINE_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(map_vars);
922  }
923 
924  return variant(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(map_vars);
992  }
993 
994  return variant(list_vars);
995 }
996 
997 DEFINE_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  std::vector<variant> result(items.begin(), it);
1011  return variant(result);
1012 }
1013 
1014 namespace
1015 {
1016 struct indexer
1017 {
1018  explicit indexer(std::size_t i)
1019  : i(i)
1020  {
1021  }
1022 
1023  variant operator()(const variant& v) const
1024  {
1025  if(i >= v.num_elements()) {
1026  return variant();
1027  } else {
1028  return v[i];
1029  }
1030  }
1031 
1032  std::size_t i;
1033 };
1034 
1035 /** @todo: replace with lambda? */
1036 struct comparator
1037 {
1038  bool operator()(const variant& a, const variant& b) const
1039  {
1040  return a.num_elements() < b.num_elements();
1041  }
1042 };
1043 
1044 std::vector<variant> get_input(
1045  const function_expression::args_list& args,
1046  const formula_callable& variables,
1047  formula_debugger* fdb)
1048 {
1049  if(args.size() == 1) {
1050  const variant list = args[0]->evaluate(variables, fdb);
1051  return std::vector<variant>(list.begin(), list.end());
1052  } else {
1053  std::vector<variant> input;
1054  input.reserve(args.size());
1055 
1056  for(expression_ptr expr : args) {
1057  input.push_back(expr->evaluate(variables, fdb));
1058  }
1059 
1060  return input;
1061  }
1062 }
1063 } // end anon namespace
1064 
1066 {
1067  const std::vector<variant> input = get_input(args(), variables, fdb);
1068  std::vector<variant> output;
1069 
1070  // So basically this does [[a,b,c],[d,e,f],[x,y,z]] -> [[a,d,x],[b,e,y],[c,f,z]]
1071  // Or [[a,b,c,d],[x,y,z]] -> [[a,x],[b,y],[c,z],[d,null()]]
1072  std::size_t max_i = std::max_element(input.begin(), input.end(), comparator())->num_elements();
1073  output.reserve(max_i);
1074 
1075  for(std::size_t i = 0; i < max_i; i++) {
1076  std::vector<variant> elem(input.size());
1077  std::transform(input.begin(), input.end(), elem.begin(), indexer(i));
1078  output.emplace_back(elem);
1079  }
1080 
1081  return variant(output);
1082 }
1083 
1084 DEFINE_WFL_FUNCTION(reduce, 2, 3)
1085 {
1086  const variant items = args()[0]->evaluate(variables, fdb);
1087  variant initial = args().size() == 2 ? variant() : args()[1]->evaluate(variables, fdb);
1088 
1089  if(items.num_elements() == 0) {
1090  return initial;
1091  }
1092 
1093  variant_iterator it = items.begin();
1094  variant res(initial.is_null() ? *it : initial);
1095  if(res != initial) {
1096  ++it;
1097  }
1098 
1099  map_formula_callable self_callable;
1100  for(; it != items.end(); ++it) {
1101  self_callable.add("a", res);
1102  self_callable.add("b", *it);
1103  res = args().back()->evaluate(
1104  formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
1105  }
1106 
1107  return res;
1108 }
1109 
1111 {
1112  variant res(0);
1113  const variant items = args()[0]->evaluate(variables, fdb);
1114  if(items.num_elements() > 0) {
1115  if(items[0].is_list()) {
1116  std::vector<variant> tmp;
1117  res = variant(tmp);
1118  if(args().size() >= 2) {
1119  res = args()[1]->evaluate(variables, fdb);
1120  if(!res.is_list())
1121  return variant();
1122  }
1123  } else if(items[0].is_map()) {
1124  std::map<variant, variant> tmp;
1125  res = variant(tmp);
1126  if(args().size() >= 2) {
1127  res = args()[1]->evaluate(variables, fdb);
1128  if(!res.is_map())
1129  return variant();
1130  }
1131  } else {
1132  if(args().size() >= 2) {
1133  res = args()[1]->evaluate(variables, fdb);
1134  }
1135  }
1136  }
1137 
1138  for(std::size_t n = 0; n != items.num_elements(); ++n) {
1139  res = res + items[n];
1140  }
1141 
1142  return res;
1143 }
1144 
1146 {
1147  const variant items = args()[0]->evaluate(variables, fdb);
1148  variant_iterator it = items.begin();
1149  if(it == items.end()) {
1150  return variant();
1151  }
1152 
1153  if(args().size() == 1) {
1154  return *it;
1155  }
1156 
1157  const int n = items.num_elements(), req = args()[1]->evaluate(variables, fdb).as_int();
1158  const int count = req < 0 ? n - std::min(-req, n) : std::min(req, n);
1159 
1160  variant_iterator end = it;
1161  std::advance(end, count);
1162 
1163  std::vector<variant> res;
1164  std::copy(it, end, std::back_inserter(res));
1165  return variant(res);
1166 }
1167 
1169 {
1170  const variant items = args()[0]->evaluate(variables, fdb);
1171  variant_iterator it = items.end();
1172  if(it == items.begin()) {
1173  return variant();
1174  }
1175 
1176  if(args().size() == 1) {
1177  return *--it;
1178  }
1179 
1180  const int n = items.num_elements(), req = args()[1]->evaluate(variables, fdb).as_int();
1181  const int count = req < 0 ? n - std::min(-req, n) : std::min(req, n);
1182 
1183  std::advance(it, -count);
1184  std::vector<variant> res;
1185 
1186  std::copy(it, items.end(), std::back_inserter(res));
1187  return variant(res);
1188 }
1189 
1191 {
1192  const variant items = args()[0]->evaluate(variables, fdb);
1193  return variant(static_cast<int>(items.num_elements()));
1194 }
1195 
1197 {
1198  if(!args().empty()) {
1199  for(std::size_t i = 0; i < args().size(); ++i) {
1200  args()[i]->evaluate(variables, fdb);
1201  }
1202  }
1203 
1204  return variant();
1205 }
1206 
1208 {
1209  variant decimal = args()[0]->evaluate(variables, fdb);
1210  int d = decimal.as_decimal();
1211 
1212  if((d >= 0) && (d % 1000 != 0)) {
1213  d /= 1000;
1214  return variant(++d);
1215  } else {
1216  d /= 1000;
1217  return variant(d);
1218  }
1219 }
1220 
1222 {
1223  variant decimal = args()[0]->evaluate(variables, fdb);
1224  int d = decimal.as_decimal();
1225  int f = d % 1000;
1226 
1227  if(f >= 500) {
1228  d /= 1000;
1229  return variant(++d);
1230  } else if(f <= -500) {
1231  d /= 1000;
1232  return variant(--d);
1233  } else {
1234  d /= 1000;
1235  return variant(d);
1236  }
1237 }
1238 
1240 {
1241  variant decimal = args()[0]->evaluate(variables, fdb);
1242  int d = decimal.as_decimal();
1243 
1244  if((d < 0) && (d % 1000 != 0)) {
1245  d /= 1000;
1246  return variant(--d);
1247  } else {
1248  d /= 1000;
1249  return variant(d);
1250  }
1251 }
1252 
1254 {
1255  variant decimal = args()[0]->evaluate(variables, fdb);
1256  int d = decimal.as_int();
1257 
1258  return variant(d);
1259 }
1260 
1262 {
1263  variant decimal = args()[0]->evaluate(variables, fdb);
1264  int d = decimal.as_decimal();
1265 
1266  d %= 1000;
1267  return variant(d, variant::DECIMAL_VARIANT);
1268 }
1269 
1271 {
1272  variant decimal = args()[0]->evaluate(variables, fdb);
1273  int d = decimal.as_decimal();
1274 
1275  if(d != 0) {
1276  d = d > 0 ? 1 : -1;
1277  }
1278 
1279  return variant(d);
1280 }
1281 
1282 DEFINE_WFL_FUNCTION(as_decimal, 1, 1)
1283 {
1284  variant decimal = args()[0]->evaluate(variables, fdb);
1285  int d = decimal.as_decimal();
1286 
1287  return variant(d, variant::DECIMAL_VARIANT);
1288 }
1289 
1291 {
1292  return variant(std::make_shared<location_callable>(map_location(
1293  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "loc:x")).as_int(),
1294  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "loc:y")).as_int(), wml_loc()
1295  )));
1296 }
1297 
1299 {
1300  return variant(std::make_shared<key_value_pair>(
1301  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "pair:key")),
1302  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "pair_value"))
1303  ));
1304 }
1305 
1307 {
1308  map_location loc1, loc2;
1309  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "distance_between:location_A")).try_convert<location_callable>()) {
1310  loc1 = loc_c->loc();
1311  }
1312 
1313  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "distance_between:location_B")).try_convert<location_callable>()) {
1314  loc2 = loc_c->loc();
1315  }
1316 
1317  if (!loc1.valid() || !loc2.valid()) {
1318  return variant();
1319  }
1320 
1321  return variant(distance_between(loc1, loc2));
1322 }
1323 
1324 DEFINE_WFL_FUNCTION(nearest_loc, 2, 2)
1325 {
1326  map_location loc;
1327  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location")).try_convert<location_callable>()) {
1328  loc = loc_c->loc();
1329  }
1330 
1331  if(!loc.valid()) {
1332  return variant();
1333  }
1334 
1335  const std::vector<variant> locations = args()[1]
1336  ->evaluate(variables, add_debug_info(fdb, 1, "nearest_loc:locations"))
1337  .as_list();
1338 
1339 #ifdef __cpp_lib_ranges
1340  auto nearest = std::ranges::min_element(locations, {},
1341  [&](const variant& cmp) { return distance_between(loc, cmp.convert_to<location_callable>()->loc()); });
1342 #else
1343  auto nearest = std::min_element(locations.begin(), locations.end(), [&](const variant& cmp1, const variant& cmp2) {
1344  return std::less{}(
1347  );
1348  });
1349 #endif
1350 
1351  if(nearest != locations.end()) {
1352  return variant(std::make_shared<location_callable>(nearest->convert_to<location_callable>()->loc()));
1353  } else {
1354  return variant();
1355  }
1356 }
1357 
1358 DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
1359 {
1360  map_location loc;
1361  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).try_convert<location_callable>()) {
1362  loc = loc_c->loc();
1363  }
1364 
1365  if(!loc.valid()) {
1366  return variant();
1367  }
1368 
1369  std::vector<variant> v;
1370  for(const map_location& adj : get_adjacent_tiles(loc)) {
1371  v.emplace_back(std::make_shared<location_callable>(adj));
1372  }
1373 
1374  return variant(v);
1375 }
1376 
1377 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
1378 {
1379  map_location loc;
1380  if (auto loc_c = args()[0]->evaluate(variables, fdb).try_convert<location_callable>()) {
1381  loc = loc_c->loc();
1382  }
1383 
1384  if(!loc.valid()) {
1385  return variant();
1386  }
1387 
1388  int range = args()[1]->evaluate(variables, fdb).as_int();
1389 
1390  if(range < 0) {
1391  return variant();
1392  }
1393 
1394  if(!range) {
1395  return variant(std::make_shared<location_callable>(loc));
1396  }
1397 
1398  std::vector<map_location> res;
1399 
1400  get_tiles_in_radius(loc, range, res);
1401 
1402  std::vector<variant> v;
1403  v.reserve(res.size() + 1);
1404  v.emplace_back(std::make_shared<location_callable>(loc));
1405 
1406  for(std::size_t n = 0; n != res.size(); ++n) {
1407  v.emplace_back(std::make_shared<location_callable>(res[n]));
1408  }
1409 
1410  return variant(v);
1411 }
1412 
1413 DEFINE_WFL_FUNCTION(are_adjacent, 2, 2)
1414 {
1415  map_location loc1, loc2;
1416  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "are_adjacent:location_A")).try_convert<location_callable>()) {
1417  loc1 = loc_c->loc();
1418  }
1419 
1420  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B")).try_convert<location_callable>()) {
1421  loc2 = loc_c->loc();
1422  }
1423 
1424  if (!loc1.valid() || !loc2.valid()) {
1425  return variant();
1426  }
1427 
1428  return variant(tiles_adjacent(loc1, loc2) ? 1 : 0);
1429 }
1430 
1431 DEFINE_WFL_FUNCTION(relative_dir, 2, 2)
1432 {
1433  map_location loc1, loc2;
1434  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "relative_dir:location_A")).try_convert<location_callable>()) {
1435  loc1 = loc_c->loc();
1436  }
1437 
1438  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B")).try_convert<location_callable>()) {
1439  loc2 = loc_c->loc();
1440  }
1441 
1442  if (!loc1.valid() || !loc2.valid()) {
1443  return variant();
1444  }
1445 
1447 }
1448 
1449 DEFINE_WFL_FUNCTION(direction_from, 2, 3)
1450 {
1451  map_location loc;
1452  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:location")).try_convert<location_callable>()) {
1453  loc = loc_c->loc();
1454  }
1455 
1456  const std::string dir_str =
1457  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:dir")).as_string();
1458 
1459  int n = args().size() == 3
1460  ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1461  : 1;
1462 
1463  if (!loc.valid()) {
1464  return variant();
1465  }
1466  return variant(std::make_shared<location_callable>(loc.get_direction(map_location::parse_direction(dir_str), n)));
1467 }
1468 
1469 DEFINE_WFL_FUNCTION(rotate_loc_around, 2, 3)
1470 {
1471  map_location center, loc;
1472  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:center")).try_convert<location_callable>()) {
1473  center = loc_c->loc();
1474  }
1475 
1476  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location")).try_convert<location_callable>()) {
1477  loc = loc_c->loc();
1478  }
1479 
1480  if (!center.valid() || !loc.valid()) {
1481  return variant();
1482  }
1483 
1484  int n = args().size() == 3
1485  ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1486  : 1;
1487 
1488  return variant(std::make_shared<location_callable>(loc.rotate_right_around_center(center, n)));
1489 }
1490 
1492 {
1493  const variant& v = args()[0]->evaluate(variables, fdb);
1494  return variant(v.type_string());
1495 }
1496 
1497 } // namespace builtins
1498 
1499 namespace actions
1500 {
1501 DEFINE_WFL_FUNCTION(safe_call, 2, 2)
1502 {
1503  const variant main = args()[0]->evaluate(variables, fdb);
1504  const expression_ptr backup_formula = args()[1];
1505 
1506  return variant(std::make_shared<safe_call_callable>(main, backup_formula));
1507 }
1508 
1509 DEFINE_WFL_FUNCTION(set_var, 2, 2)
1510 {
1511  return variant(std::make_shared<set_var_callable>(
1512  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "set_var:key")).as_string(),
1513  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "set_var:value"))));
1514 }
1515 
1516 } // namespace actions
1517 
1518 variant key_value_pair::get_value(const std::string& key) const
1519 {
1520  if(key == "key") {
1521  return key_;
1522  } else if(key == "value") {
1523  return value_;
1524  }
1525 
1526  return variant();
1527 }
1528 
1529 void key_value_pair::get_inputs(formula_input_vector& inputs) const
1530 {
1531  add_input(inputs, "key");
1532  add_input(inputs, "value");
1533 }
1534 
1535 void key_value_pair::serialize_to_string(std::string& str) const
1536 {
1537  str += "pair(";
1538  str += key_.serialize_to_string();
1539  str += ",";
1540  str += value_.serialize_to_string();
1541  str += ")";
1542 }
1543 
1544 formula_function_expression::formula_function_expression(const std::string& name,
1545  const args_list& args,
1547  const_formula_ptr precondition,
1548  const std::vector<std::string>& arg_names)
1549  : function_expression(name, args, arg_names.size(), arg_names.size())
1550  , formula_(std::move(formula))
1551  , precondition_(std::move(precondition))
1552  , arg_names_(arg_names)
1553  , star_arg_(-1)
1554 {
1555  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1556  if(arg_names_[n].empty() == false && arg_names_[n].back() == '*') {
1557  arg_names_[n].resize(arg_names_[n].size() - 1);
1558  star_arg_ = n;
1559  break;
1560  }
1561  }
1562 }
1563 
1565 {
1566  static std::string indent;
1567  indent += " ";
1568 
1569  DBG_NG << indent << "executing '" << formula_->str() << "'";
1570 
1571  const auto begin_time = std::chrono::steady_clock::now();
1572  map_formula_callable callable;
1573 
1574  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1575  variant var = args()[n]->evaluate(variables, fdb);
1576  callable.add(arg_names_[n], var);
1577 
1578  if(static_cast<int>(n) == star_arg_) {
1579  callable.set_fallback(var.as_callable());
1580  }
1581  }
1582 
1583  if(precondition_) {
1584  if(!precondition_->evaluate(callable, fdb).as_bool()) {
1585  DBG_NG << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
1586 
1587  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1588  DBG_NG << " arg " << (n + 1) << ": " << args()[n]->evaluate(variables, fdb).to_debug_string();
1589  }
1590  }
1591  }
1592 
1593  variant res = formula_->evaluate(callable, fdb);
1594 
1595  const auto taken = std::chrono::steady_clock::now() - begin_time;
1596  DBG_NG << indent << "returning: " << taken.count();
1597 
1598  indent.resize(indent.size() - 2);
1599 
1600  return res;
1601 }
1602 
1604  const std::vector<expression_ptr>& args) const
1605 {
1606  return std::make_shared<formula_function_expression>(name_, args, formula_, precondition_, args_);
1607 }
1608 
1609 function_symbol_table::function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1610  : parent(parent ? parent : get_builtins())
1611 {
1612 }
1613 
1614 void function_symbol_table::add_function(const std::string& name, formula_function_ptr&& fcn)
1615 {
1616  custom_formulas_.insert_or_assign(name, std::move(fcn));
1617 }
1618 
1620  const std::string& fn, const std::vector<expression_ptr>& args) const
1621 {
1622  const auto i = custom_formulas_.find(fn);
1623  if(i != custom_formulas_.end()) {
1624  return i->second->generate_function_expression(args);
1625  }
1626 
1627  if(parent) {
1628  expression_ptr res(parent->create_function(fn, args));
1629  if(res) {
1630  return res;
1631  }
1632  }
1633 
1634  throw formula_error("Unknown function: " + fn, "", "", 0);
1635 }
1636 
1637 std::set<std::string> function_symbol_table::get_function_names() const
1638 {
1639  std::set<std::string> res;
1640  if(parent) {
1641  res = parent->get_function_names();
1642  }
1643 
1644  for(const auto& formula : custom_formulas_) {
1645  res.insert(formula.first);
1646  }
1647 
1648  return res;
1649 }
1650 
1651 std::shared_ptr<function_symbol_table> function_symbol_table::get_builtins()
1652 {
1653  static function_symbol_table functions_table(builtins_tag);
1654 
1655  if(functions_table.empty()) {
1656  functions_table.parent = nullptr;
1657 
1658  using namespace builtins;
1660  DECLARE_WFL_FUNCTION(dir);
1662  DECLARE_WFL_FUNCTION(switch);
1663  DECLARE_WFL_FUNCTION(abs);
1664  DECLARE_WFL_FUNCTION(min);
1665  DECLARE_WFL_FUNCTION(max);
1666  DECLARE_WFL_FUNCTION(choose);
1667  DECLARE_WFL_FUNCTION(debug_float);
1668  DECLARE_WFL_FUNCTION(debug_print);
1669  DECLARE_WFL_FUNCTION(debug_profile);
1670  DECLARE_WFL_FUNCTION(wave);
1671  DECLARE_WFL_FUNCTION(sort);
1672  DECLARE_WFL_FUNCTION(contains_string);
1673  DECLARE_WFL_FUNCTION(find_string);
1677  DECLARE_WFL_FUNCTION(map);
1678  DECLARE_WFL_FUNCTION(zip);
1679  DECLARE_WFL_FUNCTION(take_while);
1680  DECLARE_WFL_FUNCTION(reduce);
1681  DECLARE_WFL_FUNCTION(sum);
1682  DECLARE_WFL_FUNCTION(head);
1683  DECLARE_WFL_FUNCTION(tail);
1685  DECLARE_WFL_FUNCTION(null);
1686  DECLARE_WFL_FUNCTION(ceil);
1687  DECLARE_WFL_FUNCTION(floor);
1688  DECLARE_WFL_FUNCTION(trunc);
1689  DECLARE_WFL_FUNCTION(frac);
1691  DECLARE_WFL_FUNCTION(round);
1692  DECLARE_WFL_FUNCTION(as_decimal);
1693  DECLARE_WFL_FUNCTION(pair);
1696  DECLARE_WFL_FUNCTION(nearest_loc);
1697  DECLARE_WFL_FUNCTION(adjacent_locs);
1698  DECLARE_WFL_FUNCTION(locations_in_radius);
1699  DECLARE_WFL_FUNCTION(are_adjacent);
1700  DECLARE_WFL_FUNCTION(relative_dir);
1701  DECLARE_WFL_FUNCTION(direction_from);
1702  DECLARE_WFL_FUNCTION(rotate_loc_around);
1703  DECLARE_WFL_FUNCTION(index_of);
1706  DECLARE_WFL_FUNCTION(tolist);
1707  DECLARE_WFL_FUNCTION(tomap);
1708  DECLARE_WFL_FUNCTION(substring);
1709  DECLARE_WFL_FUNCTION(replace);
1710  DECLARE_WFL_FUNCTION(replace_all);
1711  DECLARE_WFL_FUNCTION(starts_with);
1712  DECLARE_WFL_FUNCTION(ends_with);
1713  DECLARE_WFL_FUNCTION(length);
1714  DECLARE_WFL_FUNCTION(byte_index);
1715  DECLARE_WFL_FUNCTION(concatenate);
1716  DECLARE_WFL_FUNCTION(sin);
1717  DECLARE_WFL_FUNCTION(cos);
1718  DECLARE_WFL_FUNCTION(tan);
1719  DECLARE_WFL_FUNCTION(asin);
1720  DECLARE_WFL_FUNCTION(acos);
1721  DECLARE_WFL_FUNCTION(atan);
1722  DECLARE_WFL_FUNCTION(sqrt);
1723  DECLARE_WFL_FUNCTION(cbrt);
1724  DECLARE_WFL_FUNCTION(root);
1725  DECLARE_WFL_FUNCTION(log);
1726  DECLARE_WFL_FUNCTION(exp);
1728  DECLARE_WFL_FUNCTION(hypot);
1730  DECLARE_WFL_FUNCTION(lerp);
1731  DECLARE_WFL_FUNCTION(lerp_index);
1732  DECLARE_WFL_FUNCTION(clamp);
1733  DECLARE_WFL_FUNCTION(get_palette);
1734  }
1735 
1736  return std::shared_ptr<function_symbol_table>(&functions_table, [](function_symbol_table*) {});
1737 }
1738 
1739 action_function_symbol_table::action_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1741 {
1742  using namespace actions;
1743  function_symbol_table& functions_table = *this;
1744  DECLARE_WFL_FUNCTION(safe_call);
1745  DECLARE_WFL_FUNCTION(set_var);
1746 }
1747 }
map_location loc
Definition: move.cpp:172
#define debug(x)
std::unique_ptr< PangoAttribute, void(*)(PangoAttribute *)> value_
Definition: attributes.cpp:60
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)
Definition: function.cpp:1739
variant evaluate(const formula_callable &variables, formula_debugger *fdb=nullptr) const
Definition: function.hpp:75
std::vector< std::string > arg_names_
Definition: function.hpp:167
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: function.cpp:1564
const_formula_ptr precondition_
Definition: function.hpp:165
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()
Definition: function.cpp:1651
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1619
function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
Definition: function.cpp:1609
void add_function(const std::string &name, formula_function_ptr &&fcn)
Definition: function.cpp:1614
std::set< std::string > get_function_names() const
Definition: function.cpp:1637
std::shared_ptr< function_symbol_table > parent
Definition: function.hpp:251
functions_map custom_formulas_
Definition: function.hpp:252
const map_location & loc() const
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
void set_fallback(const_formula_callable_ptr fallback)
Definition: callable.hpp:265
function_expression_ptr generate_function_expression(const std::vector< expression_ptr > &args) const
Definition: function.cpp:1603
const_formula_ptr precondition_
Definition: function.hpp:210
const_formula_ptr formula_
Definition: function.hpp:209
std::vector< std::string > args_
Definition: function.hpp:211
Iterator class for the variant.
Definition: variant.hpp:194
bool is_map() const
Definition: variant.hpp:68
bool is_list() const
Definition: variant.hpp:66
variant get_keys() const
Definition: variant.cpp:228
variant_iterator begin() const
Definition: variant.cpp:252
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition: variant.cpp:291
variant get_values() const
Definition: variant.cpp:240
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:300
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:90
std::size_t num_elements() const
Definition: variant.cpp:267
bool is_decimal() const
Definition: variant.hpp:64
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:107
bool is_empty() const
Definition: variant.cpp:262
variant get_member(const std::string &name) const
Definition: variant.cpp:276
const std::string & as_string() const
Definition: variant.cpp:318
std::string string_cast() const
Definition: variant.cpp:638
variant_iterator end() const
Definition: variant.cpp:257
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
bool is_string() const
Definition: variant.hpp:67
std::string type_string() const
Gets string name of the current value type.
Definition: variant.hpp:155
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:643
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:62
bool is_int() const
Definition: variant.hpp:63
Formula AI debugger.
variant a_
Definition: function.cpp:821
const formula_callable * fallback_
Definition: function.cpp:820
#define LOG_SF
Definition: function.cpp:39
static lg::log_domain log_engine("engine")
std::size_t i
Definition: function.cpp:1032
static lg::log_domain log_scripting_formula("scripting/formula")
#define DBG_NG
Definition: function.cpp:37
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 int indent
Definition: log.cpp:88
static std::ostream & output()
Definition: log.cpp:103
Standard logging facilities (interface).
CURSOR_TYPE get()
Definition: cursor.cpp:218
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
bool is_map(const std::string &filename)
Returns true if the file ends with the mapfile extension.
std::vector< color_t > red_green_scale_text
const std::vector< color_t > & tc_info(std::string_view name)
const bool & debug
Definition: game_config.cpp:95
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
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
Definition: unicode.cpp:100
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:81
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
constexpr auto transform
Definition: ranges.hpp:41
constexpr auto values
Definition: ranges.hpp:42
constexpr auto reverse
Definition: ranges.hpp:40
constexpr auto keys
Definition: ranges.hpp:39
constexpr auto filter
Definition: ranges.hpp:38
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:141
Definition: callable.hpp:26
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
Definition: formula_fwd.hpp:24
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:172
std::shared_ptr< formula_function > formula_function_ptr
Definition: function.hpp:229
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: sdl2.cpp:25
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:61
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 map_location::direction n
static map_location::direction s
#define d
#define f
#define b