The Battle for Wesnoth  1.19.19+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(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 
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(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 = (*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(std::move(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(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 
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(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 
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(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 
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  return variant(std::vector(items.begin(), it));
1011 }
1012 
1013 namespace
1014 {
1015 struct 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? */
1035 struct comparator
1036 {
1037  bool operator()(const variant& a, const variant& b) const
1038  {
1039  return a.num_elements() < b.num_elements();
1040  }
1041 };
1042 
1043 std::vector<variant> get_input(
1044  const function_expression::args_list& args,
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 
1083 DEFINE_WFL_FUNCTION(reduce, 2, 3)
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;
1264  return variant(d, variant::DECIMAL_VARIANT);
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 
1279 DEFINE_WFL_FUNCTION(as_decimal, 1, 1)
1280 {
1281  variant decimal = args()[0]->evaluate(variables, fdb);
1282  int d = decimal.as_decimal();
1283 
1284  return variant(d, variant::DECIMAL_VARIANT);
1285 }
1286 
1288 {
1289  return variant(std::make_shared<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 variant(std::make_shared<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 = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "distance_between:location_A")).try_convert<location_callable>()) {
1307  loc1 = loc_c->loc();
1308  }
1309 
1310  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "distance_between:location_B")).try_convert<location_callable>()) {
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 
1321 DEFINE_WFL_FUNCTION(nearest_loc, 2, 2)
1322 {
1323  map_location loc;
1324  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location")).try_convert<location_callable>()) {
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, cmp.convert_to<location_callable>()->loc()); });
1339 #else
1340  auto nearest = std::min_element(locations.begin(), locations.end(), [&](const variant& cmp1, const variant& cmp2) {
1341  return std::less{}(
1344  );
1345  });
1346 #endif
1347 
1348  if(nearest != locations.end()) {
1349  return variant(std::make_shared<location_callable>(nearest->convert_to<location_callable>()->loc()));
1350  } else {
1351  return variant();
1352  }
1353 }
1354 
1355 DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
1356 {
1357  map_location loc;
1358  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).try_convert<location_callable>()) {
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 
1374 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
1375 {
1376  map_location loc;
1377  if (auto loc_c = args()[0]->evaluate(variables, fdb).try_convert<location_callable>()) {
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 variant(std::make_shared<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 
1410 DEFINE_WFL_FUNCTION(are_adjacent, 2, 2)
1411 {
1412  map_location loc1, loc2;
1413  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "are_adjacent:location_A")).try_convert<location_callable>()) {
1414  loc1 = loc_c->loc();
1415  }
1416 
1417  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B")).try_convert<location_callable>()) {
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 
1428 DEFINE_WFL_FUNCTION(relative_dir, 2, 2)
1429 {
1430  map_location loc1, loc2;
1431  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "relative_dir:location_A")).try_convert<location_callable>()) {
1432  loc1 = loc_c->loc();
1433  }
1434 
1435  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B")).try_convert<location_callable>()) {
1436  loc2 = loc_c->loc();
1437  }
1438 
1439  if (!loc1.valid() || !loc2.valid()) {
1440  return variant();
1441  }
1442 
1444 }
1445 
1446 DEFINE_WFL_FUNCTION(direction_from, 2, 3)
1447 {
1448  map_location loc;
1449  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:location")).try_convert<location_callable>()) {
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 variant(std::make_shared<location_callable>(loc.get_direction(map_location::parse_direction(dir_str), n)));
1464 }
1465 
1466 DEFINE_WFL_FUNCTION(rotate_loc_around, 2, 3)
1467 {
1468  map_location center, loc;
1469  if (auto loc_c = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:center")).try_convert<location_callable>()) {
1470  center = loc_c->loc();
1471  }
1472 
1473  if (auto loc_c = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location")).try_convert<location_callable>()) {
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 variant(std::make_shared<location_callable>(loc.rotate_right_around_center(center, n)));
1486 }
1487 
1489 {
1490  const variant& v = args()[0]->evaluate(variables, fdb);
1491  return variant(v.type_string());
1492 }
1493 
1494 } // namespace builtins
1495 
1496 namespace actions
1497 {
1498 DEFINE_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 variant(std::make_shared<safe_call_callable>(main, backup_formula));
1504 }
1505 
1506 DEFINE_WFL_FUNCTION(set_var, 2, 2)
1507 {
1508  return variant(std::make_shared<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 
1515 variant 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 
1526 void key_value_pair::get_inputs(formula_input_vector& inputs) const
1527 {
1528  add_input(inputs, "key");
1529  add_input(inputs, "value");
1530 }
1531 
1532 void key_value_pair::serialize_to_string(std::string& str) const
1533 {
1534  str += "pair(";
1535  str += key_.serialize_to_string();
1536  str += ",";
1537  str += value_.serialize_to_string();
1538  str += ")";
1539 }
1540 
1541 formula_function_expression::formula_function_expression(const std::string& name,
1542  const args_list& args,
1544  const_formula_ptr precondition,
1545  const std::vector<std::string>& arg_names)
1546  : function_expression(name, args, arg_names.size(), arg_names.size())
1547  , formula_(std::move(formula))
1548  , precondition_(std::move(precondition))
1549  , arg_names_(arg_names)
1550  , star_arg_(-1)
1551 {
1552  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1553  if(arg_names_[n].empty() == false && arg_names_[n].back() == '*') {
1554  arg_names_[n].resize(arg_names_[n].size() - 1);
1555  star_arg_ = n;
1556  break;
1557  }
1558  }
1559 }
1560 
1562 {
1563  static std::string indent;
1564  indent += " ";
1565 
1566  DBG_NG << indent << "executing '" << formula_->str() << "'";
1567 
1568  const auto begin_time = std::chrono::steady_clock::now();
1569  map_formula_callable callable;
1570 
1571  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1572  variant var = args()[n]->evaluate(variables, fdb);
1573  callable.add(arg_names_[n], var);
1574 
1575  if(static_cast<int>(n) == star_arg_) {
1576  callable.set_fallback(var.as_callable());
1577  }
1578  }
1579 
1580  if(precondition_) {
1581  if(!precondition_->evaluate(callable, fdb).as_bool()) {
1582  DBG_NG << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
1583 
1584  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1585  DBG_NG << " arg " << (n + 1) << ": " << args()[n]->evaluate(variables, fdb).to_debug_string();
1586  }
1587  }
1588  }
1589 
1590  variant res = formula_->evaluate(callable, fdb);
1591 
1592  const auto taken = std::chrono::steady_clock::now() - begin_time;
1593  DBG_NG << indent << "returning: " << taken.count();
1594 
1595  indent.resize(indent.size() - 2);
1596 
1597  return res;
1598 }
1599 
1601  const std::vector<expression_ptr>& args) const
1602 {
1603  return std::make_shared<formula_function_expression>(name_, args, formula_, precondition_, args_);
1604 }
1605 
1606 function_symbol_table::function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1607  : parent(parent ? parent : get_builtins())
1608 {
1609 }
1610 
1611 void function_symbol_table::add_function(const std::string& name, formula_function_ptr&& fcn)
1612 {
1613  custom_formulas_.insert_or_assign(name, std::move(fcn));
1614 }
1615 
1617  const std::string& fn, const std::vector<expression_ptr>& args) const
1618 {
1619  const auto i = custom_formulas_.find(fn);
1620  if(i != custom_formulas_.end()) {
1621  return i->second->generate_function_expression(args);
1622  }
1623 
1624  if(parent) {
1625  expression_ptr res(parent->create_function(fn, args));
1626  if(res) {
1627  return res;
1628  }
1629  }
1630 
1631  throw formula_error("Unknown function: " + fn, "", "", 0);
1632 }
1633 
1634 std::set<std::string> function_symbol_table::get_function_names() const
1635 {
1636  std::set<std::string> res;
1637  if(parent) {
1638  res = parent->get_function_names();
1639  }
1640 
1641  for(const auto& formula : custom_formulas_) {
1642  res.insert(formula.first);
1643  }
1644 
1645  return res;
1646 }
1647 
1648 std::shared_ptr<function_symbol_table> function_symbol_table::get_builtins()
1649 {
1650  static function_symbol_table functions_table(builtins_tag);
1651 
1652  if(functions_table.empty()) {
1653  functions_table.parent = nullptr;
1654 
1655  using namespace builtins;
1657  DECLARE_WFL_FUNCTION(dir);
1659  DECLARE_WFL_FUNCTION(switch);
1660  DECLARE_WFL_FUNCTION(abs);
1661  DECLARE_WFL_FUNCTION(min);
1662  DECLARE_WFL_FUNCTION(max);
1663  DECLARE_WFL_FUNCTION(choose);
1664  DECLARE_WFL_FUNCTION(debug_float);
1665  DECLARE_WFL_FUNCTION(debug_print);
1666  DECLARE_WFL_FUNCTION(debug_profile);
1667  DECLARE_WFL_FUNCTION(wave);
1668  DECLARE_WFL_FUNCTION(sort);
1669  DECLARE_WFL_FUNCTION(contains_string);
1670  DECLARE_WFL_FUNCTION(find_string);
1674  DECLARE_WFL_FUNCTION(map);
1675  DECLARE_WFL_FUNCTION(zip);
1676  DECLARE_WFL_FUNCTION(take_while);
1677  DECLARE_WFL_FUNCTION(reduce);
1678  DECLARE_WFL_FUNCTION(sum);
1679  DECLARE_WFL_FUNCTION(head);
1680  DECLARE_WFL_FUNCTION(tail);
1682  DECLARE_WFL_FUNCTION(null);
1683  DECLARE_WFL_FUNCTION(ceil);
1684  DECLARE_WFL_FUNCTION(floor);
1685  DECLARE_WFL_FUNCTION(trunc);
1686  DECLARE_WFL_FUNCTION(frac);
1688  DECLARE_WFL_FUNCTION(round);
1689  DECLARE_WFL_FUNCTION(as_decimal);
1690  DECLARE_WFL_FUNCTION(pair);
1693  DECLARE_WFL_FUNCTION(nearest_loc);
1694  DECLARE_WFL_FUNCTION(adjacent_locs);
1695  DECLARE_WFL_FUNCTION(locations_in_radius);
1696  DECLARE_WFL_FUNCTION(are_adjacent);
1697  DECLARE_WFL_FUNCTION(relative_dir);
1698  DECLARE_WFL_FUNCTION(direction_from);
1699  DECLARE_WFL_FUNCTION(rotate_loc_around);
1700  DECLARE_WFL_FUNCTION(index_of);
1703  DECLARE_WFL_FUNCTION(tolist);
1704  DECLARE_WFL_FUNCTION(tomap);
1705  DECLARE_WFL_FUNCTION(substring);
1706  DECLARE_WFL_FUNCTION(replace);
1707  DECLARE_WFL_FUNCTION(replace_all);
1708  DECLARE_WFL_FUNCTION(starts_with);
1709  DECLARE_WFL_FUNCTION(ends_with);
1710  DECLARE_WFL_FUNCTION(length);
1711  DECLARE_WFL_FUNCTION(byte_index);
1712  DECLARE_WFL_FUNCTION(concatenate);
1713  DECLARE_WFL_FUNCTION(sin);
1714  DECLARE_WFL_FUNCTION(cos);
1715  DECLARE_WFL_FUNCTION(tan);
1716  DECLARE_WFL_FUNCTION(asin);
1717  DECLARE_WFL_FUNCTION(acos);
1718  DECLARE_WFL_FUNCTION(atan);
1719  DECLARE_WFL_FUNCTION(sqrt);
1720  DECLARE_WFL_FUNCTION(cbrt);
1721  DECLARE_WFL_FUNCTION(root);
1722  DECLARE_WFL_FUNCTION(log);
1723  DECLARE_WFL_FUNCTION(exp);
1725  DECLARE_WFL_FUNCTION(hypot);
1727  DECLARE_WFL_FUNCTION(lerp);
1728  DECLARE_WFL_FUNCTION(lerp_index);
1729  DECLARE_WFL_FUNCTION(clamp);
1730  DECLARE_WFL_FUNCTION(get_palette);
1731  }
1732 
1733  return std::shared_ptr<function_symbol_table>(&functions_table, [](function_symbol_table*) {});
1734 }
1735 
1736 action_function_symbol_table::action_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1738 {
1739  using namespace actions;
1740  function_symbol_table& functions_table = *this;
1741  DECLARE_WFL_FUNCTION(safe_call);
1742  DECLARE_WFL_FUNCTION(set_var);
1743 }
1744 }
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:1736
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:1561
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:1648
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1616
function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
Definition: function.cpp:1606
void add_function(const std::string &name, formula_function_ptr &&fcn)
Definition: function.cpp:1611
std::set< std::string > get_function_names() const
Definition: function.cpp:1634
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:1600
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:197
bool is_map() const
Definition: variant.hpp:71
bool is_list() const
Definition: variant.hpp:69
variant get_keys() const
Definition: variant.cpp:237
variant_iterator begin() const
Definition: variant.cpp:261
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition: variant.cpp:300
variant get_values() const
Definition: variant.cpp:249
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:309
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:93
std::size_t num_elements() const
Definition: variant.cpp:276
bool is_decimal() const
Definition: variant.hpp:67
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:110
bool is_empty() const
Definition: variant.cpp:271
variant get_member(const std::string &name) const
Definition: variant.cpp:285
const std::string & as_string() const
Definition: variant.cpp:327
std::string string_cast() const
Definition: variant.cpp:647
variant_iterator end() const
Definition: variant.cpp:266
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:322
bool is_string() const
Definition: variant.hpp:70
std::string type_string() const
Gets string name of the current value type.
Definition: variant.hpp:158
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:652
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:65
bool is_int() const
Definition: variant.hpp:66
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:1031
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:206
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:45
constexpr auto values
Definition: ranges.hpp:46
constexpr auto reverse
Definition: ranges.hpp:44
constexpr auto keys
Definition: ranges.hpp:43
constexpr auto filter
Definition: ranges.hpp:42
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: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 map_location::direction n
static map_location::direction s
#define d
#define f
#define b