The Battle for Wesnoth  1.19.11+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"
19 #include "formula/callable_objects.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  game_display::get_singleton()->float_label(location, text, color_t(255, 0, 0));
244 }
245 } // end anon namespace
246 
247 DEFINE_WFL_FUNCTION(debug_float, 2, 3)
248 {
249  const args_list& arguments = args();
250  const variant var0 = arguments[0]->evaluate(variables, fdb);
251  variant var1 = arguments[1]->evaluate(variables, fdb);
252 
253  const map_location location = var0.convert_to<location_callable>()->loc();
254  std::string text;
255 
256  if(arguments.size() == 2) {
257  text = var1.to_debug_string();
258  display_float(location, text);
259  return var1;
260  } else {
261  const variant var2 = arguments[2]->evaluate(variables, fdb);
262  text = var1.string_cast() + var2.to_debug_string();
263  display_float(location, text);
264  return var2;
265  }
266 }
267 
268 DEFINE_WFL_FUNCTION(debug_print, 1, 2)
269 {
270  std::string speaker = "WFL";
271  int i_value = 0;
272 
273  if(args().size() == 2) {
274  speaker = args()[0]->evaluate(variables, fdb).string_cast();
275  i_value = 1;
276  }
277 
278  variant value = args()[i_value]->evaluate(variables, fdb);
279  const std::string str = value.to_debug_string(true);
280 
281  LOG_SF << speaker << ": " << str;
282 
285  std::time(nullptr), speaker, 0, str, events::chat_handler::MESSAGE_PUBLIC, false);
286  }
287 
288  return value;
289 }
290 
291 DEFINE_WFL_FUNCTION(debug_profile, 1, 2)
292 {
293  std::string speaker = "WFL";
294  int i_value = 0;
295 
296  if(args().size() == 2) {
297  speaker = args()[0]->evaluate(variables, fdb).string_cast();
298  i_value = 1;
299  }
300 
301  const variant value = args()[i_value]->evaluate(variables, fdb);
302  const int run_count = 1000;
303  std::chrono::steady_clock::duration run_time;
304 
305  for(int i = 0; i < run_count; i++) {
306  const auto start = std::chrono::steady_clock::now();
307  args()[i_value]->evaluate(variables, fdb);
308  run_time += std::chrono::steady_clock::now() - start;
309  }
310 
311  // Average execution time over all runs
312  auto average_ms = std::chrono::duration_cast<std::chrono::milliseconds>(run_time / run_count);
313 
314  std::ostringstream str;
315 #ifdef __cpp_lib_format
316  str << "Evaluated in " << average_ms << " on average";
317 #else
318  str << "Evaluated in " << average_ms.count() << " ms on average";
319 #endif
320 
321  LOG_SF << speaker << ": " << str.str();
322 
325  std::time(nullptr), speaker, 0, str.str(), events::chat_handler::MESSAGE_PUBLIC, false);
326  }
327 
328  return value;
329 }
330 
332 {
333  const variant map = args()[0]->evaluate(variables, fdb);
334  return map.get_keys();
335 }
336 
338 {
339  const variant map = args()[0]->evaluate(variables, fdb);
340  return map.get_values();
341 }
342 
343 DEFINE_WFL_FUNCTION(tolist, 1, 1)
344 {
345  const variant var = args()[0]->evaluate(variables, fdb);
346 
347  std::vector<variant> tmp;
348  for(variant_iterator it = var.begin(); it != var.end(); ++it) {
349  tmp.push_back(*it);
350  }
351 
352  return variant(tmp);
353 }
354 
356 {
357  const variant var_1 = args()[0]->evaluate(variables, fdb);
358 
359  std::map<variant, variant> tmp;
360 
361  if(args().size() == 2) {
362  const variant var_2 = args()[1]->evaluate(variables, fdb);
363  if(var_1.num_elements() != var_2.num_elements()) {
364  return variant();
365  }
366 
367  for(std::size_t i = 0; i < var_1.num_elements(); ++i) {
368  tmp[var_1[i]] = var_2[i];
369  }
370  } else {
371  for(variant_iterator it = var_1.begin(); it != var_1.end(); ++it) {
372  if(auto kv = (*it).try_convert<key_value_pair>()) {
373  tmp[kv->query_value("key")] = kv->query_value("value");
374  } else {
375  auto map_it = tmp.find(*it);
376 
377  if(map_it == tmp.end()) {
378  tmp[*it] = variant(1);
379  } else {
380  map_it->second = variant(map_it->second.as_int() + 1);
381  }
382  }
383  }
384  }
385 
386  return variant(tmp);
387 }
388 
389 DEFINE_WFL_FUNCTION(substring, 2, 3)
390 {
391  std::string result = args()[0]->evaluate(variables, fdb).as_string();
392 
393  int offset = args()[1]->evaluate(variables, fdb).as_int();
394  if(offset < 0) {
395  offset += result.size();
396 
397  if(offset < 0) {
398  offset = 0;
399  }
400  } else {
401  if(static_cast<std::size_t>(offset) >= result.size()) {
402  return variant(std::string());
403  }
404  }
405 
406  if(args().size() > 2) {
407  int size = args()[2]->evaluate(variables, fdb).as_int();
408 
409  if(size < 0) {
410  size = -size;
411  offset = std::max(0, offset - size + 1);
412  }
413 
414  return variant(result.substr(offset, size));
415  }
416 
417  return variant(result.substr(offset));
418 }
419 
420 DEFINE_WFL_FUNCTION(replace, 3, 4)
421 {
422  std::string result = args()[0]->evaluate(variables, fdb).as_string();
423  std::string replacement = args().back()->evaluate(variables, fdb).as_string();
424 
425  int offset = args()[1]->evaluate(variables, fdb).as_int();
426  if(offset < 0) {
427  offset += result.size();
428 
429  if(offset < 0) {
430  offset = 0;
431  }
432  } else {
433  if(static_cast<std::size_t>(offset) >= result.size()) {
434  return variant(result);
435  }
436  }
437 
438  if(args().size() > 3) {
439  int size = args()[2]->evaluate(variables, fdb).as_int();
440 
441  if(size < 0) {
442  size = -size;
443  offset = std::max(0, offset - size + 1);
444  }
445 
446  return variant(result.replace(offset, size, replacement));
447  }
448 
449  return variant(result.replace(offset, std::string::npos, replacement));
450 }
451 
452 DEFINE_WFL_FUNCTION(replace_all, 3, 3)
453 {
454  std::string result = args()[0]->evaluate(variables, fdb).as_string();
455  std::string needle = args()[1]->evaluate(variables, fdb).as_string();
456  std::string replacement = args().back()->evaluate(variables, fdb).as_string();
457  boost::replace_all(result, needle, replacement);
458  return variant(result);
459 }
460 
461 DEFINE_WFL_FUNCTION(starts_with, 2, 2)
462 {
463  std::string str = args()[0]->evaluate(variables, fdb).as_string();
464  std::string prefix = args()[1]->evaluate(variables, fdb).as_string();
465  return variant(boost::starts_with(str, prefix));
466 }
467 
468 DEFINE_WFL_FUNCTION(ends_with, 2, 2)
469 {
470  std::string str = args()[0]->evaluate(variables, fdb).as_string();
471  std::string prefix = args()[1]->evaluate(variables, fdb).as_string();
472  return variant(boost::ends_with(str, prefix));
473 }
474 
476 {
477  std::string result = args()[0]->evaluate(variables, fdb).as_string();
478  std::string insert = args().back()->evaluate(variables, fdb).as_string();
479 
480  int offset = args()[1]->evaluate(variables, fdb).as_int();
481  if(offset < 0) {
482  offset += result.size();
483 
484  if(offset < 0) {
485  offset = 0;
486  }
487  } else if(static_cast<std::size_t>(offset) >= result.size()) {
488  return variant(result + insert);
489  }
490 
491  return variant(result.insert(offset, insert));
492 }
493 
494 DEFINE_WFL_FUNCTION(length, 1, 1)
495 {
496  return variant(args()[0]->evaluate(variables, fdb).as_string().length());
497 }
498 
499 DEFINE_WFL_FUNCTION(byte_index, 2, 2)
500 {
501  return variant(utf8::index(
502  args()[0]->evaluate(variables, fdb).as_string(),
503  args()[1]->evaluate(variables, fdb).as_int()));
504 }
505 
506 DEFINE_WFL_FUNCTION(concatenate, 1, -1)
507 {
508  std::string result;
509  for(expression_ptr arg : args()) {
510  result += arg->evaluate(variables, fdb).string_cast();
511  }
512 
513  return variant(result);
514 }
515 
516 DEFINE_WFL_FUNCTION(str_upper, 1, 1)
517 {
518  std::string str = args()[0]->evaluate(variables, fdb).as_string();
519  std::transform(str.begin(), str.end(), str.begin(), static_cast<int (*)(int)>(std::toupper));
520  return variant(str);
521 }
522 
523 DEFINE_WFL_FUNCTION(str_lower, 1, 1)
524 {
525  std::string str = args()[0]->evaluate(variables, fdb).as_string();
526  std::transform(str.begin(), str.end(), str.begin(), static_cast<int (*)(int)>(std::tolower));
527  return variant(str);
528 }
529 
531 {
532  const double angle = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
533  const double result = std::sin(angle * pi<double>() / 180.0);
534  return variant(result, variant::DECIMAL_VARIANT);
535 }
536 
538 {
539  const double angle = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
540  const double result = std::cos(angle * pi<double>() / 180.0);
541  return variant(result, variant::DECIMAL_VARIANT);
542 }
543 
545 {
546  const double angle = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
547  const double result = std::tan(angle * pi<double>() / 180.0);
548  if(std::isnan(result) || result <= std::numeric_limits<int>::min() || result >= std::numeric_limits<int>::max()) {
549  return variant();
550  }
551 
552  return variant(result, variant::DECIMAL_VARIANT);
553 }
554 
556 {
557  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
558  const double result = std::asin(num) * 180.0 / pi<double>();
559  if(std::isnan(result)) {
560  return variant();
561  }
562 
563  return variant(result, variant::DECIMAL_VARIANT);
564 }
565 
567 {
568  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
569  const double result = std::acos(num) * 180.0 / pi<double>();
570  if(std::isnan(result)) {
571  return variant();
572  }
573 
574  return variant(result, variant::DECIMAL_VARIANT);
575 }
576 
578 {
579  if(args().size() == 1) {
580  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
581  const double result = std::atan(num) * 180.0 / pi<double>();
582  return variant(result, variant::DECIMAL_VARIANT);
583  } else {
584  const double y = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
585  const double x = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
586  const double result = std::atan2(y, x) * 180.0 / pi<double>();
587  return variant(result, variant::DECIMAL_VARIANT);
588  }
589 }
590 
592 {
593  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
594  const double result = std::sqrt(num);
595  if(std::isnan(result)) {
596  return variant();
597  }
598 
599  return variant(result, variant::DECIMAL_VARIANT);
600 }
601 
603 {
604  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
605  const double result = num < 0 ? -std::pow(-num, 1.0 / 3.0) : std::pow(num, 1.0 / 3.0);
606  return variant(result, variant::DECIMAL_VARIANT);
607 }
608 
610 {
611  const double base = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
612  const double root = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
613  const double result = base < 0 && std::fmod(root, 2) == 1 ? -std::pow(-base, 1.0 / root) : std::pow(base, 1.0 / root);
614  if(std::isnan(result)) {
615  return variant();
616  }
617 
618  return variant(result, variant::DECIMAL_VARIANT);
619 }
620 
622 {
623  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
624  if(args().size() == 1) {
625  const double result = std::log(num);
626  if(std::isnan(result)) {
627  return variant();
628  }
629 
630  return variant(result, variant::DECIMAL_VARIANT);
631  }
632 
633  const double base = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
634  const double result = std::log(num) / std::log(base);
635  if(std::isnan(result)) {
636  return variant();
637  }
638 
639  return variant(result, variant::DECIMAL_VARIANT);
640 }
641 
643 {
644  const double num = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
645  const double result = std::exp(num);
646  if(result == 0 || result >= std::numeric_limits<int>::max()) {
647  // These are range errors rather than NaNs,
648  // but I figure it's better than returning INT_MIN.
649  return variant();
650  }
651 
652  return variant(result, variant::DECIMAL_VARIANT);
653 }
654 
656 {
657  return variant(pi<double>(), variant::DECIMAL_VARIANT);
658 }
659 
661 {
662  const double x = args()[0]->evaluate(variables, fdb).as_decimal() / 1000.0;
663  const double y = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
664  return variant(std::hypot(x, y), variant::DECIMAL_VARIANT);
665 }
666 
667 DEFINE_WFL_FUNCTION(index_of, 2, 2)
668 {
669  const variant value = args()[0]->evaluate(variables, fdb);
670  const variant list = args()[1]->evaluate(variables, fdb);
671 
672  for(std::size_t i = 0; i < list.num_elements(); ++i) {
673  if(list[i] == value) {
674  return variant(i);
675  }
676  }
677 
678  return variant(-1);
679 }
680 
681 DEFINE_WFL_FUNCTION(choose, 2, 3)
682 {
683  const variant items = args()[0]->evaluate(variables, fdb);
684  variant max_value;
685  variant_iterator max;
686 
687  if(args().size() == 2) {
688  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
689  const variant val = args().back()->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
690 
691  if(max == variant_iterator() || val > max_value) {
692  max = it;
693  max_value = val;
694  }
695  }
696  } else {
697  map_formula_callable self_callable;
698  const std::string self = args()[1]->evaluate(variables, fdb).as_string();
699 
700  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
701  self_callable.add(self, *it);
702 
703  const variant val = args().back()->evaluate(
704  formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
705 
706  if(max == variant_iterator() || val > max_value) {
707  max = it;
708  max_value = val;
709  }
710  }
711  }
712 
713  if(max == variant_iterator()) {
714  return variant();
715  }
716 
717  return *max;
718 }
719 
721 {
722  const int value = args()[0]->evaluate(variables, fdb).as_int() % 1000;
723  const double angle = 2.0 * pi<double>() * (static_cast<double>(value) / 1000.0);
724  return variant(static_cast<int>(std::sin(angle) * 1000.0));
725 }
726 
728 {
729  const double lo = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "lerp:lo")).as_decimal() / 1000.0;
730  const double hi = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "lerp:hi")).as_decimal() / 1000.0;;
731  const double alpha = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "lerp:alpha")).as_decimal() / 1000.0;;
732  return variant(static_cast<int>((lo + alpha * (hi - lo)) * 1000.0), variant::DECIMAL_VARIANT);
733 }
734 
735 DEFINE_WFL_FUNCTION(lerp_index, 2, 2)
736 {
737  const std::vector<variant> items = args()[0]->evaluate(variables, fdb).as_list();
738  if(items.empty()) return variant();
739  const double alpha = args()[1]->evaluate(variables, fdb).as_decimal() / 1000.0;
740  // Same formula as red_to_green etc
741  const double val_scaled = std::clamp(0.01 * alpha, 0.0, 1.0);
742  const int idx = int(std::nearbyint((items.size() - 1) * val_scaled));
743  return items[idx];
744 }
745 
746 DEFINE_WFL_FUNCTION(get_palette, 1, 1)
747 {
748  const std::string name = args()[0]->evaluate(variables, fdb).as_string();
749  std::vector<color_t> colors;
750  if(name == "red_green_scale") {
752  } else if(name == "red_green_scale_text") {
754  } else if(name == "blue_white_scale") {
756  } else if(name == "blue_white_scale_text") {
758  } else {
759  colors = game_config::tc_info(name);
760  }
761  std::vector<variant> result;
762  result.reserve(colors.size());
763  for(auto clr : colors) {
764  result.emplace_back(std::make_shared<color_callable>(clr));
765  }
766  return variant(result);
767 }
768 
770 {
771  const variant val = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "clamp:value"));
772  const variant lo = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "clamp:lo"));
773  const variant hi = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "clamp:hi"));
774  if(val.is_int() && lo.is_int() && hi.is_int()) {
775  return variant(std::clamp<int>(val.as_int(), lo.as_int(), hi.as_int()));
776  }
777  return variant(static_cast<int>(std::clamp<int>(val.as_decimal(), lo.as_decimal(), hi.as_decimal())), variant::DECIMAL_VARIANT);
778 }
779 
780 namespace
781 {
782 class variant_comparator : public formula_callable
783 {
784 public:
785  variant_comparator(const expression_ptr& expr, const formula_callable& fallback)
786  : expr_(expr)
787  , fallback_(&fallback)
788  , a_()
789  , b_()
790  {
791  }
792 
793  bool operator()(const variant& a, const variant& b) const
794  {
795  a_ = a;
796  b_ = b;
797  return expr_->evaluate(*this).as_bool();
798  }
799 
800 private:
801  variant get_value(const std::string& key) const
802  {
803  if(key == "a") {
804  return a_;
805  } else if(key == "b") {
806  return b_;
807  } else {
808  return fallback_->query_value(key);
809  }
810  }
811 
812  void get_inputs(formula_input_vector& inputs) const
813  {
814  fallback_->get_inputs(inputs);
815  }
816 
818  const formula_callable* fallback_;
819  mutable variant a_, b_;
820 };
821 } // end anon namespace
822 
824 {
825  variant list = args()[0]->evaluate(variables, fdb);
826 
827  std::vector<variant> vars;
828  vars.reserve(list.num_elements());
829 
830  for(std::size_t n = 0; n != list.num_elements(); ++n) {
831  vars.push_back(list[n]);
832  }
833 
834  if(args().size() == 1) {
835  std::sort(vars.begin(), vars.end());
836  } else {
837  std::sort(vars.begin(), vars.end(), variant_comparator(args()[1], variables));
838  }
839 
840  return variant(vars);
841 }
842 
844 {
845  const variant& arg = args()[0]->evaluate(variables, fdb);
846 
847  if(arg.is_string()) {
848  std::string str = args()[0]->evaluate(variables, fdb).as_string();
849  std::reverse(str.begin(), str.end());
850 
851  return variant(str);
852  } else if(arg.is_list()) {
853  std::vector<variant> list = args()[0]->evaluate(variables, fdb).as_list();
854  std::reverse(list.begin(), list.end());
855 
856  return variant(list);
857  }
858 
859  return variant();
860 }
861 
862 DEFINE_WFL_FUNCTION(contains_string, 2, 2)
863 {
864  std::string str = args()[0]->evaluate(variables, fdb).as_string();
865  std::string key = args()[1]->evaluate(variables, fdb).as_string();
866 
867  return variant(str.find(key) != std::string::npos);
868 }
869 
870 DEFINE_WFL_FUNCTION(find_string, 2, 2)
871 {
872  const std::string str = args()[0]->evaluate(variables, fdb).as_string();
873  const std::string key = args()[1]->evaluate(variables, fdb).as_string();
874 
875  std::size_t pos = str.find(key);
876  return variant(static_cast<int>(pos));
877 }
878 
880 {
881  std::vector<variant> list_vars;
882  std::map<variant, variant> map_vars;
883 
884  const variant items = args()[0]->evaluate(variables, fdb);
885 
886  if(args().size() == 2) {
887  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
888  const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
889 
890  if(val.as_bool()) {
891  if(items.is_map()) {
892  map_vars[(*it).get_member("key")] = (*it).get_member("value");
893  } else {
894  list_vars.push_back(*it);
895  }
896  }
897  }
898  } else {
899  map_formula_callable self_callable;
900  const std::string self = args()[1]->evaluate(variables, fdb).as_string();
901 
902  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
903  self_callable.add(self, *it);
904 
905  const variant val = args()[2]->evaluate(
906  formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
907 
908  if(val.as_bool()) {
909  if(items.is_map()) {
910  map_vars[(*it).get_member("key")] = (*it).get_member("value");
911  } else {
912  list_vars.push_back(*it);
913  }
914  }
915  }
916  }
917 
918  if(items.is_map()) {
919  return variant(map_vars);
920  }
921 
922  return variant(list_vars);
923 }
924 
926 {
927  const variant items = args()[0]->evaluate(variables, fdb);
928 
929  if(args().size() == 2) {
930  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
931  const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
932  if(val.as_bool()) {
933  return *it;
934  }
935  }
936  } else {
937  map_formula_callable self_callable;
938  const std::string self = args()[1]->evaluate(variables, fdb).as_string();
939 
940  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
941  self_callable.add(self, *it);
942 
943  const variant val = args().back()->evaluate(
944  formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
945 
946  if(val.as_bool()) {
947  return *it;
948  }
949  }
950  }
951 
952  return variant();
953 }
954 
956 {
957  std::vector<variant> list_vars;
958  std::map<variant, variant> map_vars;
959  const variant items = args()[0]->evaluate(variables, fdb);
960 
961  if(args().size() == 2) {
962  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
963  const variant val = args().back()->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
964  if(items.is_map()) {
965  map_vars[(*it).get_member("key")] = val;
966  } else {
967  list_vars.push_back(val);
968  }
969  }
970  } else {
971  map_formula_callable self_callable;
972  const std::string self = args()[1]->evaluate(variables, fdb).as_string();
973 
974  for(variant_iterator it = items.begin(); it != items.end(); ++it) {
975  self_callable.add(self, *it);
976 
977  const variant val = args().back()->evaluate(
978  formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
979 
980  if(items.is_map()) {
981  map_vars[(*it).get_member("key")] = val;
982  } else {
983  list_vars.push_back(val);
984  }
985  }
986  }
987 
988  if(items.is_map()) {
989  return variant(map_vars);
990  }
991 
992  return variant(list_vars);
993 }
994 
995 DEFINE_WFL_FUNCTION(take_while, 2, 2)
996 {
997  const variant& items = args()[0]->evaluate(variables, fdb);
998 
999  variant_iterator it = items.begin();
1000  for(; it != items.end(); ++it) {
1001  const variant matches = args().back()->evaluate(formula_variant_callable_with_backup(*it, variables), fdb);
1002 
1003  if(!matches.as_bool()) {
1004  break;
1005  }
1006  }
1007 
1008  std::vector<variant> result(items.begin(), it);
1009  return variant(result);
1010 }
1011 
1012 namespace
1013 {
1014 struct indexer
1015 {
1016  explicit indexer(std::size_t i)
1017  : i(i)
1018  {
1019  }
1020 
1021  variant operator()(const variant& v) const
1022  {
1023  if(i >= v.num_elements()) {
1024  return variant();
1025  } else {
1026  return v[i];
1027  }
1028  }
1029 
1030  std::size_t i;
1031 };
1032 
1033 /** @todo: replace with lambda? */
1034 struct comparator
1035 {
1036  bool operator()(const variant& a, const variant& b) const
1037  {
1038  return a.num_elements() < b.num_elements();
1039  }
1040 };
1041 
1042 std::vector<variant> get_input(
1043  const function_expression::args_list& args,
1044  const formula_callable& variables,
1045  formula_debugger* fdb)
1046 {
1047  if(args.size() == 1) {
1048  const variant list = args[0]->evaluate(variables, fdb);
1049  return std::vector<variant>(list.begin(), list.end());
1050  } else {
1051  std::vector<variant> input;
1052  input.reserve(args.size());
1053 
1054  for(expression_ptr expr : args) {
1055  input.push_back(expr->evaluate(variables, fdb));
1056  }
1057 
1058  return input;
1059  }
1060 }
1061 } // end anon namespace
1062 
1064 {
1065  const std::vector<variant> input = get_input(args(), variables, fdb);
1066  std::vector<variant> output;
1067 
1068  // So basically this does [[a,b,c],[d,e,f],[x,y,z]] -> [[a,d,x],[b,e,y],[c,f,z]]
1069  // Or [[a,b,c,d],[x,y,z]] -> [[a,x],[b,y],[c,z],[d,null()]]
1070  std::size_t max_i = std::max_element(input.begin(), input.end(), comparator())->num_elements();
1071  output.reserve(max_i);
1072 
1073  for(std::size_t i = 0; i < max_i; i++) {
1074  std::vector<variant> elem(input.size());
1075  std::transform(input.begin(), input.end(), elem.begin(), indexer(i));
1076  output.emplace_back(elem);
1077  }
1078 
1079  return variant(output);
1080 }
1081 
1082 DEFINE_WFL_FUNCTION(reduce, 2, 3)
1083 {
1084  const variant items = args()[0]->evaluate(variables, fdb);
1085  variant initial = args().size() == 2 ? variant() : args()[1]->evaluate(variables, fdb);
1086 
1087  if(items.num_elements() == 0) {
1088  return initial;
1089  }
1090 
1091  variant_iterator it = items.begin();
1092  variant res(initial.is_null() ? *it : initial);
1093  if(res != initial) {
1094  ++it;
1095  }
1096 
1097  map_formula_callable self_callable;
1098  for(; it != items.end(); ++it) {
1099  self_callable.add("a", res);
1100  self_callable.add("b", *it);
1101  res = args().back()->evaluate(
1102  formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(*it, variables)), fdb);
1103  }
1104 
1105  return res;
1106 }
1107 
1109 {
1110  variant res(0);
1111  const variant items = args()[0]->evaluate(variables, fdb);
1112  if(items.num_elements() > 0) {
1113  if(items[0].is_list()) {
1114  std::vector<variant> tmp;
1115  res = variant(tmp);
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  std::map<variant, variant> tmp;
1123  res = variant(tmp);
1124  if(args().size() >= 2) {
1125  res = args()[1]->evaluate(variables, fdb);
1126  if(!res.is_map())
1127  return variant();
1128  }
1129  } else {
1130  if(args().size() >= 2) {
1131  res = args()[1]->evaluate(variables, fdb);
1132  }
1133  }
1134  }
1135 
1136  for(std::size_t n = 0; n != items.num_elements(); ++n) {
1137  res = res + items[n];
1138  }
1139 
1140  return res;
1141 }
1142 
1144 {
1145  const variant items = args()[0]->evaluate(variables, fdb);
1146  variant_iterator it = items.begin();
1147  if(it == items.end()) {
1148  return variant();
1149  }
1150 
1151  if(args().size() == 1) {
1152  return *it;
1153  }
1154 
1155  const int n = items.num_elements(), req = args()[1]->evaluate(variables, fdb).as_int();
1156  const int count = req < 0 ? n - std::min(-req, n) : std::min(req, n);
1157 
1158  variant_iterator end = it;
1159  std::advance(end, count);
1160 
1161  std::vector<variant> res;
1162  std::copy(it, end, std::back_inserter(res));
1163  return variant(res);
1164 }
1165 
1167 {
1168  const variant items = args()[0]->evaluate(variables, fdb);
1169  variant_iterator it = items.end();
1170  if(it == items.begin()) {
1171  return variant();
1172  }
1173 
1174  if(args().size() == 1) {
1175  return *--it;
1176  }
1177 
1178  const int n = items.num_elements(), req = args()[1]->evaluate(variables, fdb).as_int();
1179  const int count = req < 0 ? n - std::min(-req, n) : std::min(req, n);
1180 
1181  std::advance(it, -count);
1182  std::vector<variant> res;
1183 
1184  std::copy(it, items.end(), std::back_inserter(res));
1185  return variant(res);
1186 }
1187 
1189 {
1190  const variant items = args()[0]->evaluate(variables, fdb);
1191  return variant(static_cast<int>(items.num_elements()));
1192 }
1193 
1195 {
1196  if(!args().empty()) {
1197  for(std::size_t i = 0; i < args().size(); ++i) {
1198  args()[i]->evaluate(variables, fdb);
1199  }
1200  }
1201 
1202  return variant();
1203 }
1204 
1206 {
1207  variant decimal = args()[0]->evaluate(variables, fdb);
1208  int d = decimal.as_decimal();
1209 
1210  if((d >= 0) && (d % 1000 != 0)) {
1211  d /= 1000;
1212  return variant(++d);
1213  } else {
1214  d /= 1000;
1215  return variant(d);
1216  }
1217 }
1218 
1220 {
1221  variant decimal = args()[0]->evaluate(variables, fdb);
1222  int d = decimal.as_decimal();
1223  int f = d % 1000;
1224 
1225  if(f >= 500) {
1226  d /= 1000;
1227  return variant(++d);
1228  } else if(f <= -500) {
1229  d /= 1000;
1230  return variant(--d);
1231  } else {
1232  d /= 1000;
1233  return variant(d);
1234  }
1235 }
1236 
1238 {
1239  variant decimal = args()[0]->evaluate(variables, fdb);
1240  int d = decimal.as_decimal();
1241 
1242  if((d < 0) && (d % 1000 != 0)) {
1243  d /= 1000;
1244  return variant(--d);
1245  } else {
1246  d /= 1000;
1247  return variant(d);
1248  }
1249 }
1250 
1252 {
1253  variant decimal = args()[0]->evaluate(variables, fdb);
1254  int d = decimal.as_int();
1255 
1256  return variant(d);
1257 }
1258 
1260 {
1261  variant decimal = args()[0]->evaluate(variables, fdb);
1262  int d = decimal.as_decimal();
1263 
1264  d %= 1000;
1265  return variant(d, variant::DECIMAL_VARIANT);
1266 }
1267 
1269 {
1270  variant decimal = args()[0]->evaluate(variables, fdb);
1271  int d = decimal.as_decimal();
1272 
1273  if(d != 0) {
1274  d = d > 0 ? 1 : -1;
1275  }
1276 
1277  return variant(d);
1278 }
1279 
1280 DEFINE_WFL_FUNCTION(as_decimal, 1, 1)
1281 {
1282  variant decimal = args()[0]->evaluate(variables, fdb);
1283  int d = decimal.as_decimal();
1284 
1285  return variant(d, variant::DECIMAL_VARIANT);
1286 }
1287 
1289 {
1290  return variant(std::make_shared<location_callable>(map_location(
1291  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "loc:x")).as_int(),
1292  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "loc:y")).as_int(), wml_loc()
1293  )));
1294 }
1295 
1297 {
1298  return variant(std::make_shared<key_value_pair>(
1299  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "pair:key")),
1300  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "pair_value"))
1301  ));
1302 }
1303 
1305 {
1306  const map_location loc1 = args()[0]
1307  ->evaluate(variables, add_debug_info(fdb, 0, "distance_between:location_A"))
1308  .convert_to<location_callable>()
1309  ->loc();
1310 
1311  const map_location loc2 = args()[1]
1312  ->evaluate(variables, add_debug_info(fdb, 1, "distance_between:location_B"))
1313  .convert_to<location_callable>()
1314  ->loc();
1315 
1316  return variant(distance_between(loc1, loc2));
1317 }
1318 
1319 DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
1320 {
1321  const map_location loc = args()[0]
1322  ->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location"))
1323  .convert_to<location_callable>()
1324  ->loc();
1325 
1326  std::vector<variant> v;
1327  for(const map_location& adj : get_adjacent_tiles(loc)) {
1328  v.emplace_back(std::make_shared<location_callable>(adj));
1329  }
1330 
1331  return variant(v);
1332 }
1333 
1334 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
1335 {
1336  const map_location loc = args()[0]->evaluate(variables, fdb).convert_to<location_callable>()->loc();
1337 
1338  int range = args()[1]->evaluate(variables, fdb).as_int();
1339 
1340  if(range < 0) {
1341  return variant();
1342  }
1343 
1344  if(!range) {
1345  return variant(std::make_shared<location_callable>(loc));
1346  }
1347 
1348  std::vector<map_location> res;
1349 
1350  get_tiles_in_radius(loc, range, res);
1351 
1352  std::vector<variant> v;
1353  v.reserve(res.size() + 1);
1354  v.emplace_back(std::make_shared<location_callable>(loc));
1355 
1356  for(std::size_t n = 0; n != res.size(); ++n) {
1357  v.emplace_back(std::make_shared<location_callable>(res[n]));
1358  }
1359 
1360  return variant(v);
1361 }
1362 
1363 DEFINE_WFL_FUNCTION(are_adjacent, 2, 2)
1364 {
1365  const map_location loc1 = args()[0]
1366  ->evaluate(variables, add_debug_info(fdb, 0, "are_adjacent:location_A"))
1367  .convert_to<location_callable>()
1368  ->loc();
1369 
1370  const map_location loc2 = args()[1]
1371  ->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B"))
1372  .convert_to<location_callable>()
1373  ->loc();
1374 
1375  return variant(tiles_adjacent(loc1, loc2) ? 1 : 0);
1376 }
1377 
1378 DEFINE_WFL_FUNCTION(relative_dir, 2, 2)
1379 {
1380  const map_location loc1 = args()[0]
1381  ->evaluate(variables, add_debug_info(fdb, 0, "relative_dir:location_A"))
1382  .convert_to<location_callable>()
1383  ->loc();
1384 
1385  const map_location loc2 = args()[1]
1386  ->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B"))
1387  .convert_to<location_callable>()
1388  ->loc();
1389 
1391 }
1392 
1393 DEFINE_WFL_FUNCTION(direction_from, 2, 3)
1394 {
1395  const map_location loc = args()[0]
1396  ->evaluate(variables, add_debug_info(fdb, 0, "direction_from:location"))
1397  .convert_to<location_callable>()
1398  ->loc();
1399 
1400  const std::string dir_str =
1401  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:dir")).as_string();
1402 
1403  int n = args().size() == 3
1404  ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1405  : 1;
1406 
1407  return variant(std::make_shared<location_callable>(loc.get_direction(map_location::parse_direction(dir_str), n)));
1408 }
1409 
1410 DEFINE_WFL_FUNCTION(rotate_loc_around, 2, 3)
1411 {
1412  const map_location center = args()[0]
1413  ->evaluate(variables, add_debug_info(fdb, 0, "direction_from:center"))
1414  .convert_to<location_callable>()
1415  ->loc();
1416 
1417  const map_location loc = args()[1]
1418  ->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location"))
1419  .convert_to<location_callable>()
1420  ->loc();
1421 
1422  int n = args().size() == 3
1423  ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int()
1424  : 1;
1425 
1426  return variant(std::make_shared<location_callable>(loc.rotate_right_around_center(center, n)));
1427 }
1428 
1430 {
1431  const variant& v = args()[0]->evaluate(variables, fdb);
1432  return variant(v.type_string());
1433 }
1434 
1435 } // namespace builtins
1436 
1437 namespace actions
1438 {
1439 DEFINE_WFL_FUNCTION(safe_call, 2, 2)
1440 {
1441  const variant main = args()[0]->evaluate(variables, fdb);
1442  const expression_ptr backup_formula = args()[1];
1443 
1444  return variant(std::make_shared<safe_call_callable>(main, backup_formula));
1445 }
1446 
1447 DEFINE_WFL_FUNCTION(set_var, 2, 2)
1448 {
1449  return variant(std::make_shared<set_var_callable>(
1450  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "set_var:key")).as_string(),
1451  args()[1]->evaluate(variables, add_debug_info(fdb, 1, "set_var:value"))));
1452 }
1453 
1454 } // namespace actions
1455 
1456 variant key_value_pair::get_value(const std::string& key) const
1457 {
1458  if(key == "key") {
1459  return key_;
1460  } else if(key == "value") {
1461  return value_;
1462  }
1463 
1464  return variant();
1465 }
1466 
1467 void key_value_pair::get_inputs(formula_input_vector& inputs) const
1468 {
1469  add_input(inputs, "key");
1470  add_input(inputs, "value");
1471 }
1472 
1473 void key_value_pair::serialize_to_string(std::string& str) const
1474 {
1475  str += "pair(";
1476  str += key_.serialize_to_string();
1477  str += ",";
1478  str += value_.serialize_to_string();
1479  str += ")";
1480 }
1481 
1482 formula_function_expression::formula_function_expression(const std::string& name,
1483  const args_list& args,
1485  const_formula_ptr precondition,
1486  const std::vector<std::string>& arg_names)
1487  : function_expression(name, args, arg_names.size(), arg_names.size())
1488  , formula_(std::move(formula))
1489  , precondition_(std::move(precondition))
1490  , arg_names_(arg_names)
1491  , star_arg_(-1)
1492 {
1493  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1494  if(arg_names_[n].empty() == false && arg_names_[n].back() == '*') {
1495  arg_names_[n].resize(arg_names_[n].size() - 1);
1496  star_arg_ = n;
1497  break;
1498  }
1499  }
1500 }
1501 
1503 {
1504  static std::string indent;
1505  indent += " ";
1506 
1507  DBG_NG << indent << "executing '" << formula_->str() << "'";
1508 
1509  const auto begin_time = std::chrono::steady_clock::now();
1510  map_formula_callable callable;
1511 
1512  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1513  variant var = args()[n]->evaluate(variables, fdb);
1514  callable.add(arg_names_[n], var);
1515 
1516  if(static_cast<int>(n) == star_arg_) {
1517  callable.set_fallback(var.as_callable());
1518  }
1519  }
1520 
1521  if(precondition_) {
1522  if(!precondition_->evaluate(callable, fdb).as_bool()) {
1523  DBG_NG << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
1524 
1525  for(std::size_t n = 0; n != arg_names_.size(); ++n) {
1526  DBG_NG << " arg " << (n + 1) << ": " << args()[n]->evaluate(variables, fdb).to_debug_string();
1527  }
1528  }
1529  }
1530 
1531  variant res = formula_->evaluate(callable, fdb);
1532 
1533  const auto taken = std::chrono::steady_clock::now() - begin_time;
1534  DBG_NG << indent << "returning: " << taken.count();
1535 
1536  indent.resize(indent.size() - 2);
1537 
1538  return res;
1539 }
1540 
1542  const std::vector<expression_ptr>& args) const
1543 {
1544  return std::make_shared<formula_function_expression>(name_, args, formula_, precondition_, args_);
1545 }
1546 
1547 function_symbol_table::function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1548  : parent(parent ? parent : get_builtins())
1549 {
1550 }
1551 
1552 void function_symbol_table::add_function(const std::string& name, formula_function_ptr&& fcn)
1553 {
1554  custom_formulas_.insert_or_assign(name, std::move(fcn));
1555 }
1556 
1558  const std::string& fn, const std::vector<expression_ptr>& args) const
1559 {
1560  const auto i = custom_formulas_.find(fn);
1561  if(i != custom_formulas_.end()) {
1562  return i->second->generate_function_expression(args);
1563  }
1564 
1565  if(parent) {
1566  expression_ptr res(parent->create_function(fn, args));
1567  if(res) {
1568  return res;
1569  }
1570  }
1571 
1572  throw formula_error("Unknown function: " + fn, "", "", 0);
1573 }
1574 
1575 std::set<std::string> function_symbol_table::get_function_names() const
1576 {
1577  std::set<std::string> res;
1578  if(parent) {
1579  res = parent->get_function_names();
1580  }
1581 
1582  for(const auto& formula : custom_formulas_) {
1583  res.insert(formula.first);
1584  }
1585 
1586  return res;
1587 }
1588 
1589 std::shared_ptr<function_symbol_table> function_symbol_table::get_builtins()
1590 {
1591  static function_symbol_table functions_table(builtins_tag);
1592 
1593  if(functions_table.empty()) {
1594  functions_table.parent = nullptr;
1595 
1596  using namespace builtins;
1598  DECLARE_WFL_FUNCTION(dir);
1600  DECLARE_WFL_FUNCTION(switch);
1601  DECLARE_WFL_FUNCTION(abs);
1602  DECLARE_WFL_FUNCTION(min);
1603  DECLARE_WFL_FUNCTION(max);
1604  DECLARE_WFL_FUNCTION(choose);
1605  DECLARE_WFL_FUNCTION(debug_float);
1606  DECLARE_WFL_FUNCTION(debug_print);
1607  DECLARE_WFL_FUNCTION(debug_profile);
1608  DECLARE_WFL_FUNCTION(wave);
1609  DECLARE_WFL_FUNCTION(sort);
1610  DECLARE_WFL_FUNCTION(contains_string);
1611  DECLARE_WFL_FUNCTION(find_string);
1615  DECLARE_WFL_FUNCTION(map);
1616  DECLARE_WFL_FUNCTION(zip);
1617  DECLARE_WFL_FUNCTION(take_while);
1618  DECLARE_WFL_FUNCTION(reduce);
1619  DECLARE_WFL_FUNCTION(sum);
1620  DECLARE_WFL_FUNCTION(head);
1621  DECLARE_WFL_FUNCTION(tail);
1623  DECLARE_WFL_FUNCTION(null);
1624  DECLARE_WFL_FUNCTION(ceil);
1625  DECLARE_WFL_FUNCTION(floor);
1626  DECLARE_WFL_FUNCTION(trunc);
1627  DECLARE_WFL_FUNCTION(frac);
1629  DECLARE_WFL_FUNCTION(round);
1630  DECLARE_WFL_FUNCTION(as_decimal);
1631  DECLARE_WFL_FUNCTION(pair);
1634  DECLARE_WFL_FUNCTION(adjacent_locs);
1635  DECLARE_WFL_FUNCTION(locations_in_radius);
1636  DECLARE_WFL_FUNCTION(are_adjacent);
1637  DECLARE_WFL_FUNCTION(relative_dir);
1638  DECLARE_WFL_FUNCTION(direction_from);
1639  DECLARE_WFL_FUNCTION(rotate_loc_around);
1640  DECLARE_WFL_FUNCTION(index_of);
1643  DECLARE_WFL_FUNCTION(tolist);
1644  DECLARE_WFL_FUNCTION(tomap);
1645  DECLARE_WFL_FUNCTION(substring);
1646  DECLARE_WFL_FUNCTION(replace);
1647  DECLARE_WFL_FUNCTION(replace_all);
1648  DECLARE_WFL_FUNCTION(starts_with);
1649  DECLARE_WFL_FUNCTION(ends_with);
1650  DECLARE_WFL_FUNCTION(length);
1651  DECLARE_WFL_FUNCTION(byte_index);
1652  DECLARE_WFL_FUNCTION(concatenate);
1653  DECLARE_WFL_FUNCTION(sin);
1654  DECLARE_WFL_FUNCTION(cos);
1655  DECLARE_WFL_FUNCTION(tan);
1656  DECLARE_WFL_FUNCTION(asin);
1657  DECLARE_WFL_FUNCTION(acos);
1658  DECLARE_WFL_FUNCTION(atan);
1659  DECLARE_WFL_FUNCTION(sqrt);
1660  DECLARE_WFL_FUNCTION(cbrt);
1661  DECLARE_WFL_FUNCTION(root);
1662  DECLARE_WFL_FUNCTION(log);
1663  DECLARE_WFL_FUNCTION(exp);
1665  DECLARE_WFL_FUNCTION(hypot);
1667  DECLARE_WFL_FUNCTION(lerp);
1668  DECLARE_WFL_FUNCTION(lerp_index);
1669  DECLARE_WFL_FUNCTION(clamp);
1670  DECLARE_WFL_FUNCTION(get_palette);
1671  }
1672 
1673  return std::shared_ptr<function_symbol_table>(&functions_table, [](function_symbol_table*) {});
1674 }
1675 
1676 action_function_symbol_table::action_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent)
1678 {
1679  using namespace actions;
1680  function_symbol_table& functions_table = *this;
1681  DECLARE_WFL_FUNCTION(safe_call);
1682  DECLARE_WFL_FUNCTION(set_var);
1683 }
1684 }
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::time_t &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()
void float_label(const map_location &loc, const std::string &text, const color_t &color)
Function to float a label above a tile.
action_function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
Definition: function.cpp:1676
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:1502
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:1589
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1557
function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
Definition: function.cpp:1547
void add_function(const std::string &name, formula_function_ptr &&fcn)
Definition: function.cpp:1552
std::set< std::string > get_function_names() const
Definition: function.cpp:1575
std::shared_ptr< function_symbol_table > parent
Definition: function.hpp:251
functions_map custom_formulas_
Definition: function.hpp:252
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:1541
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:187
int as_int() const
Definition: variant.cpp:291
bool is_map() const
Definition: variant.hpp:68
bool is_list() const
Definition: variant.hpp:66
int as_decimal() const
Returns variant's internal representation of decimal number: ie, 1.234 is represented as 1234.
Definition: variant.cpp:300
variant get_keys() const
Definition: variant.cpp:228
variant_iterator begin() const
Definition: variant.cpp:252
variant get_values() const
Definition: variant.cpp:240
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:83
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:100
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:148
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:819
const formula_callable * fallback_
Definition: function.cpp:818
#define LOG_SF
Definition: function.cpp:39
static lg::log_domain log_engine("engine")
std::size_t i
Definition: function.cpp:1030
static lg::log_domain log_scripting_formula("scripting/formula")
#define DBG_NG
Definition: function.cpp:37
variant b_
Definition: function.cpp:819
expression_ptr expr_
Definition: function.cpp:817
#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:479
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:550
#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:507
static int indent
Definition: log.cpp:59
static std::ostream & output()
Definition: log.cpp:74
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: contexts.hpp:43
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:364
map_location rotate_right_around_center(const map_location &center, int k) const
Definition: location.cpp:301
direction get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:240
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