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