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