The Battle for Wesnoth  1.19.13+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  const map_location loc1 = args()[0]
1309  ->evaluate(variables, add_debug_info(fdb, 0, "distance_between:location_A"))
1310  .convert_to<location_callable>()
1311  ->loc();
1312 
1313  const map_location loc2 = args()[1]
1314  ->evaluate(variables, add_debug_info(fdb, 1, "distance_between:location_B"))
1315  .convert_to<location_callable>()
1316  ->loc();
1317 
1318  return variant(distance_between(loc1, loc2));
1319 }
1320 
1321 DEFINE_WFL_FUNCTION(nearest_loc, 2, 2)
1322 {
1323  const map_location loc = args()[0]
1324  ->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location"))
1325  .convert_to<location_callable>()
1326  ->loc();
1327 
1328  const std::vector<variant> locations = args()[1]
1329  ->evaluate(variables, add_debug_info(fdb, 1, "nearest_loc:locations"))
1330  .as_list();
1331 
1332 #ifdef __cpp_lib_ranges
1333  auto nearest = std::ranges::min_element(locations, {},
1334  [&](const variant& cmp) { return distance_between(loc, cmp.convert_to<location_callable>()->loc()); });
1335 #else
1336  auto nearest = std::min_element(locations.begin(), locations.end(), [&](const variant& cmp1, const variant& cmp2) {
1337  return std::less{}(
1340  );
1341  });
1342 #endif
1343 
1344  if(nearest != locations.end()) {
1345  return variant(std::make_shared<location_callable>(nearest->convert_to<location_callable>()->loc()));
1346  } else {
1347  return variant();
1348  }
1349 }
1350 
1351 DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
1352 {
1353  const map_location loc = args()[0]
1354  ->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location"))
1355  .convert_to<location_callable>()
1356  ->loc();
1357 
1358  std::vector<variant> v;
1359  for(const map_location& adj : get_adjacent_tiles(loc)) {
1360  v.emplace_back(std::make_shared<location_callable>(adj));
1361  }
1362 
1363  return variant(v);
1364 }
1365 
1366 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
1367 {
1368  const map_location loc = args()[0]->evaluate(variables, fdb).convert_to<location_callable>()->loc();
1369 
1370  int range = args()[1]->evaluate(variables, fdb).as_int();
1371 
1372  if(range < 0) {
1373  return variant();
1374  }
1375 
1376  if(!range) {
1377  return variant(std::make_shared<location_callable>(loc));
1378  }
1379 
1380  std::vector<map_location> res;
1381 
1382  get_tiles_in_radius(loc, range, res);
1383 
1384  std::vector<variant> v;
1385  v.reserve(res.size() + 1);
1386  v.emplace_back(std::make_shared<location_callable>(loc));
1387 
1388  for(std::size_t n = 0; n != res.size(); ++n) {
1389  v.emplace_back(std::make_shared<location_callable>(res[n]));
1390  }
1391 
1392  return variant(v);
1393 }
1394 
1395 DEFINE_WFL_FUNCTION(are_adjacent, 2, 2)
1396 {
1397  const map_location loc1 = args()[0]
1398  ->evaluate(variables, add_debug_info(fdb, 0, "are_adjacent:location_A"))
1399  .convert_to<location_callable>()
1400  ->loc();
1401 
1402  const map_location loc2 = args()[1]
1403  ->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B"))
1404  .convert_to<location_callable>()
1405  ->loc();
1406 
1407  return variant(tiles_adjacent(loc1, loc2) ? 1 : 0);
1408 }
1409 
1410 DEFINE_WFL_FUNCTION(relative_dir, 2, 2)
1411 {
1412  const map_location loc1 = args()[0]
1413  ->evaluate(variables, add_debug_info(fdb, 0, "relative_dir:location_A"))
1414  .convert_to<location_callable>()
1415  ->loc();
1416 
1417  const map_location loc2 = args()[1]
1418  ->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B"))
1419  .convert_to<location_callable>()
1420  ->loc();
1421 
1423 }
1424 
1425 DEFINE_WFL_FUNCTION(direction_from, 2, 3)
1426 {
1427  const map_location loc = args()[0]
1428  ->evaluate(variables, add_debug_info(fdb, 0, "direction_from:location"))
1429  .convert_to<location_callable>()
1430  ->loc();
1431 
1432  const std::string dir_str =
1433  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:dir")).as_string();
1434 
1435  int n = args().size() == 3
1436  ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1437  : 1;
1438 
1439  return variant(std::make_shared<location_callable>(loc.get_direction(map_location::parse_direction(dir_str), n)));
1440 }
1441 
1442 DEFINE_WFL_FUNCTION(rotate_loc_around, 2, 3)
1443 {
1444  const map_location center = args()[0]
1445  ->evaluate(variables, add_debug_info(fdb, 0, "direction_from:center"))
1446  .convert_to<location_callable>()
1447  ->loc();
1448 
1449  const map_location loc = args()[1]
1450  ->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location"))
1451  .convert_to<location_callable>()
1452  ->loc();
1453 
1454  int n = args().size() == 3
1455  ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1456  : 1;
1457 
1458  return variant(std::make_shared<location_callable>(loc.rotate_right_around_center(center, n)));
1459 }
1460 
1462 {
1463  const variant& v = args()[0]->evaluate(variables, fdb);
1464  return variant(v.type_string());
1465 }
1466 
1467 } // namespace builtins
1468 
1469 namespace actions
1470 {
1471 DEFINE_WFL_FUNCTION(safe_call, 2, 2)
1472 {
1473  const variant main = args()[0]->evaluate(variables, fdb);
1474  const expression_ptr backup_formula = args()[1];
1475 
1476  return variant(std::make_shared<safe_call_callable>(main, backup_formula));
1477 }
1478 
1479 DEFINE_WFL_FUNCTION(set_var, 2, 2)
1480 {
1481  return variant(std::make_shared<set_var_callable>(
1482  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "set_var:key")).as_string(),
1483  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "set_var:value"))));
1484 }
1485 
1486 } // namespace actions
1487 
1488 variant key_value_pair::get_value(const std::string& key) const
1489 {
1490  if(key == "key") {
1491  return key_;
1492  } else if(key == "value") {
1493  return value_;
1494  }
1495 
1496  return variant();
1497 }
1498 
1499 void key_value_pair::get_inputs(formula_input_vector& inputs) const
1500 {
1501  add_input(inputs, "key");
1502  add_input(inputs, "value");
1503 }
1504 
1505 void key_value_pair::serialize_to_string(std::string& str) const
1506 {
1507  str += "pair(";
1508  str += key_.serialize_to_string();
1509  str += ",";
1510  str += value_.serialize_to_string();
1511  str += ")";
1512 }
1513 
1514 formula_function_expression::formula_function_expression(const std::string& name,
1515  const args_list& args,
1517  const_formula_ptr precondition,
1518  const std::vector<std::string>& arg_names)
1519  : function_expression(name, args, arg_names.size(), arg_names.size())
1520  , formula_(std::move(formula))
1521  , precondition_(std::move(precondition))
1522  , arg_names_(arg_names)
1523  , star_arg_(-1)
1524 {
1525  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1526  if(arg_names_[n].empty() == false && arg_names_[n].back() == '*') {
1527  arg_names_[n].resize(arg_names_[n].size() - 1);
1528  star_arg_ = n;
1529  break;
1530  }
1531  }
1532 }
1533 
1535 {
1536  static std::string indent;
1537  indent += " ";
1538 
1539  DBG_NG << indent << "executing '" << formula_->str() << "'";
1540 
1541  const auto begin_time = std::chrono::steady_clock::now();
1542  map_formula_callable callable;
1543 
1544  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1545  variant var = args()[n]->evaluate(variables, fdb);
1546  callable.add(arg_names_[n], var);
1547 
1548  if(static_cast<int>(n) == star_arg_) {
1549  callable.set_fallback(var.as_callable());
1550  }
1551  }
1552 
1553  if(precondition_) {
1554  if(!precondition_->evaluate(callable, fdb).as_bool()) {
1555  DBG_NG << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
1556 
1557  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1558  DBG_NG << " arg " << (n + 1) << ": " << args()[n]->evaluate(variables, fdb).to_debug_string();
1559  }
1560  }
1561  }
1562 
1563  variant res = formula_->evaluate(callable, fdb);
1564 
1565  const auto taken = std::chrono::steady_clock::now() - begin_time;
1566  DBG_NG << indent << "returning: " << taken.count();
1567 
1568  indent.resize(indent.size() - 2);
1569 
1570  return res;
1571 }
1572 
1574  const std::vector<expression_ptr>& args) const
1575 {
1576  return std::make_shared<formula_function_expression>(name_, args, formula_, precondition_, args_);
1577 }
1578 
1579 function_symbol_table::function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1580  : parent(parent ? parent : get_builtins())
1581 {
1582 }
1583 
1584 void function_symbol_table::add_function(const std::string& name, formula_function_ptr&& fcn)
1585 {
1586  custom_formulas_.insert_or_assign(name, std::move(fcn));
1587 }
1588 
1590  const std::string& fn, const std::vector<expression_ptr>& args) const
1591 {
1592  const auto i = custom_formulas_.find(fn);
1593  if(i != custom_formulas_.end()) {
1594  return i->second->generate_function_expression(args);
1595  }
1596 
1597  if(parent) {
1598  expression_ptr res(parent->create_function(fn, args));
1599  if(res) {
1600  return res;
1601  }
1602  }
1603 
1604  throw formula_error("Unknown function: " + fn, "", "", 0);
1605 }
1606 
1607 std::set<std::string> function_symbol_table::get_function_names() const
1608 {
1609  std::set<std::string> res;
1610  if(parent) {
1611  res = parent->get_function_names();
1612  }
1613 
1614  for(const auto& formula : custom_formulas_) {
1615  res.insert(formula.first);
1616  }
1617 
1618  return res;
1619 }
1620 
1621 std::shared_ptr<function_symbol_table> function_symbol_table::get_builtins()
1622 {
1623  static function_symbol_table functions_table(builtins_tag);
1624 
1625  if(functions_table.empty()) {
1626  functions_table.parent = nullptr;
1627 
1628  using namespace builtins;
1630  DECLARE_WFL_FUNCTION(dir);
1632  DECLARE_WFL_FUNCTION(switch);
1633  DECLARE_WFL_FUNCTION(abs);
1634  DECLARE_WFL_FUNCTION(min);
1635  DECLARE_WFL_FUNCTION(max);
1636  DECLARE_WFL_FUNCTION(choose);
1637  DECLARE_WFL_FUNCTION(debug_float);
1638  DECLARE_WFL_FUNCTION(debug_print);
1639  DECLARE_WFL_FUNCTION(debug_profile);
1640  DECLARE_WFL_FUNCTION(wave);
1641  DECLARE_WFL_FUNCTION(sort);
1642  DECLARE_WFL_FUNCTION(contains_string);
1643  DECLARE_WFL_FUNCTION(find_string);
1647  DECLARE_WFL_FUNCTION(map);
1648  DECLARE_WFL_FUNCTION(zip);
1649  DECLARE_WFL_FUNCTION(take_while);
1650  DECLARE_WFL_FUNCTION(reduce);
1651  DECLARE_WFL_FUNCTION(sum);
1652  DECLARE_WFL_FUNCTION(head);
1653  DECLARE_WFL_FUNCTION(tail);
1655  DECLARE_WFL_FUNCTION(null);
1656  DECLARE_WFL_FUNCTION(ceil);
1657  DECLARE_WFL_FUNCTION(floor);
1658  DECLARE_WFL_FUNCTION(trunc);
1659  DECLARE_WFL_FUNCTION(frac);
1661  DECLARE_WFL_FUNCTION(round);
1662  DECLARE_WFL_FUNCTION(as_decimal);
1663  DECLARE_WFL_FUNCTION(pair);
1666  DECLARE_WFL_FUNCTION(nearest_loc);
1667  DECLARE_WFL_FUNCTION(adjacent_locs);
1668  DECLARE_WFL_FUNCTION(locations_in_radius);
1669  DECLARE_WFL_FUNCTION(are_adjacent);
1670  DECLARE_WFL_FUNCTION(relative_dir);
1671  DECLARE_WFL_FUNCTION(direction_from);
1672  DECLARE_WFL_FUNCTION(rotate_loc_around);
1673  DECLARE_WFL_FUNCTION(index_of);
1676  DECLARE_WFL_FUNCTION(tolist);
1677  DECLARE_WFL_FUNCTION(tomap);
1678  DECLARE_WFL_FUNCTION(substring);
1679  DECLARE_WFL_FUNCTION(replace);
1680  DECLARE_WFL_FUNCTION(replace_all);
1681  DECLARE_WFL_FUNCTION(starts_with);
1682  DECLARE_WFL_FUNCTION(ends_with);
1683  DECLARE_WFL_FUNCTION(length);
1684  DECLARE_WFL_FUNCTION(byte_index);
1685  DECLARE_WFL_FUNCTION(concatenate);
1686  DECLARE_WFL_FUNCTION(sin);
1687  DECLARE_WFL_FUNCTION(cos);
1688  DECLARE_WFL_FUNCTION(tan);
1689  DECLARE_WFL_FUNCTION(asin);
1690  DECLARE_WFL_FUNCTION(acos);
1691  DECLARE_WFL_FUNCTION(atan);
1692  DECLARE_WFL_FUNCTION(sqrt);
1693  DECLARE_WFL_FUNCTION(cbrt);
1694  DECLARE_WFL_FUNCTION(root);
1695  DECLARE_WFL_FUNCTION(log);
1696  DECLARE_WFL_FUNCTION(exp);
1698  DECLARE_WFL_FUNCTION(hypot);
1700  DECLARE_WFL_FUNCTION(lerp);
1701  DECLARE_WFL_FUNCTION(lerp_index);
1702  DECLARE_WFL_FUNCTION(clamp);
1703  DECLARE_WFL_FUNCTION(get_palette);
1704  }
1705 
1706  return std::shared_ptr<function_symbol_table>(&functions_table, [](function_symbol_table*) {});
1707 }
1708 
1709 action_function_symbol_table::action_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1711 {
1712  using namespace actions;
1713  function_symbol_table& functions_table = *this;
1714  DECLARE_WFL_FUNCTION(safe_call);
1715  DECLARE_WFL_FUNCTION(set_var);
1716 }
1717 }
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:1709
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:1534
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:1621
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1589
function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
Definition: function.cpp:1579
void add_function(const std::string &name, formula_function_ptr &&fcn)
Definition: function.cpp:1584
std::set< std::string > get_function_names() const
Definition: function.cpp:1607
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:1573
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
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:512
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:583
#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:540
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:98
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
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:140
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:45
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
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