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