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