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