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