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