The Battle for Wesnoth  1.19.19+dev
variant.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include <cassert>
17 #include <cmath>
18 #include <cstring>
19 #include <memory>
20 #include <stack>
21 
22 #include "formatter.hpp"
23 #include "formula/function.hpp"
24 #include "log.hpp"
25 
26 static lg::log_domain log_scripting_formula("scripting/formula");
27 #define DBG_SF LOG_STREAM(debug, log_scripting_formula)
28 #define LOG_SF LOG_STREAM(info, log_scripting_formula)
29 #define WRN_SF LOG_STREAM(warn, log_scripting_formula)
30 #define ERR_SF LOG_STREAM(err, log_scripting_formula)
31 
32 
33 namespace wfl
34 {
35 
36 // Static value to initialize null variants to ensure its value is never nullptr.
38 
40 {
42 }
43 
44 // Small helper function to get a standard type error message.
45 static std::string was_expecting(const std::string& message, const variant& v)
46 {
47  std::ostringstream ss;
48 
49  ss << "TYPE ERROR: expected " << message << " but found "
50  << v.type_string() << " (" << v.to_debug_string() << ")";
51 
52  return ss.str();
53 }
54 
55 type_error::type_error(const std::string& str) : game::error(str)
56 {
57  PLAIN_LOG << "ERROR: " << message << "\n" << call_stack_manager::get();
58 }
59 
61  : type_(formula_variant::type::null)
62  , container_(nullptr)
63  , iter_()
64 {
65 }
66 
67 variant_iterator::variant_iterator(const variant_value_base* value, const utils::any& iter)
68  : type_(value->get_type())
69  , container_(value)
70  , iter_(iter)
71 {
72 }
73 
75 {
76  if(!container_) {
77  return variant();
78  }
79 
81 }
82 
84 {
85  if(container_) {
87  }
88 
89  return *this;
90 }
91 
93 {
94  variant_iterator temp(*this);
95  if(container_) {
97  }
98 
99  return temp;
100 }
101 
103 {
104  if(container_) {
106  }
107 
108  return *this;
109 }
110 
112 {
113  variant_iterator temp(*this);
114  if(container_) {
116  }
117 
118  return temp;
119 }
120 
122 {
123  if(!container_ && !that.container_) {
124  return true;
125  }
126 
127  if(container_ == that.container_) {
128  return container_->iterator_equals(iter_, that.iter_);
129  }
130 
131  return false;
132 }
133 
135 {
136  return !operator==(that);
137 }
138 
139 
141  : value_(null_value)
142 {}
143 
145  : value_(std::make_shared<variant_int>(n))
146 {
147 }
148 
150  : value_(std::make_shared<variant_decimal>(n))
151 {
152 }
153 
155  : value_(std::make_shared<variant_decimal>(n))
156 {
157 }
158 
159 variant::variant(const std::vector<variant>& vec)
160  : value_(std::make_shared<variant_list>(vec))
161 {
162 }
163 
164 variant::variant(std::vector<variant>&& vec)
165  : value_(std::make_shared<variant_list>(std::move(vec)))
166 {
167 }
168 
169 variant::variant(const std::string& str)
170  : value_(std::make_shared<variant_string>(str))
171 {
172 }
173 
174 variant::variant(std::string&& str)
175  : value_(std::make_shared<variant_string>(std::move(str)))
176 {
177 }
178 
179 variant::variant(const std::map<variant, variant>& map)
180  : value_(std::make_shared<variant_map>(map))
181 {
182 }
183 
184 variant::variant(std::map<variant, variant>&& map)
185  : value_(std::make_shared<variant_map>(std::move(map)))
186 {
187 }
188 
189 variant variant::operator[](std::size_t n) const
190 {
191  if(is_callable()) {
192  return *this;
193  }
194 
195  must_be(formula_variant::type::list);
196 
197  try {
198  return value_cast<variant_list>()->get_container().at(n);
199  } catch(std::out_of_range&) {
200  throw type_error("invalid index");
201  }
202 }
203 
205 {
206  if(is_callable()) {
207  return *this;
208  }
209 
210  if(is_map()) {
211  auto& map = value_cast<variant_map>()->get_container();
212 
213  auto i = map.find(v);
214  if(i == map.end()) {
215  return variant();
216  }
217 
218  return i->second;
219  } else if(is_list()) {
220  if(v.is_list()) {
221  std::vector<variant> slice;
222  for(std::size_t i = 0; i < v.num_elements(); ++i) {
223  slice.push_back((*this)[v[i]]);
224  }
225 
226  return variant(std::move(slice));
227  } else if(v.as_int() < 0) {
228  return operator[](num_elements() + v.as_int());
229  }
230 
231  return operator[](v.as_int());
232  }
233 
234  throw type_error(was_expecting("a list or a map", *this));
235 }
236 
238 {
239  must_be(formula_variant::type::map);
240 
241  std::vector<variant> tmp;
242  for(const auto& i : value_cast<variant_map>()->get_container()) {
243  tmp.push_back(i.first);
244  }
245 
246  return variant(std::move(tmp));
247 }
248 
250 {
251  must_be(formula_variant::type::map);
252 
253  std::vector<variant> tmp;
254  for(const auto& i : value_cast<variant_map>()->get_container()) {
255  tmp.push_back(i.second);
256  }
257 
258  return variant(std::move(tmp));
259 }
260 
262 {
263  return value_->make_iterator().begin();
264 }
265 
267 {
268  return value_->make_iterator().end();
269 }
270 
271 bool variant::is_empty() const
272 {
273  return value_->is_empty();
274 }
275 
276 std::size_t variant::num_elements() const
277 {
278  if(!is_list() && !is_map()) {
279  throw type_error(was_expecting("a list or a map", *this));
280  }
281 
282  return value_->num_elements();
283 }
284 
285 variant variant::get_member(const std::string& name) const
286 {
287  if(is_callable()) {
288  if(auto obj = value_cast<variant_callable>()->get_callable()) {
289  return obj->query_value(name);
290  }
291  }
292 
293  if(name == "self") {
294  return *this;
295  }
296 
297  return variant();
298 }
299 
300 int variant::as_int(int fallback) const
301 {
302  if(is_null()) { return fallback; }
303  if(is_decimal()) { return as_decimal() / 1000; }
304 
305  must_be(formula_variant::type::integer);
306  return value_cast<variant_int>()->get_numeric_value();
307 }
308 
309 int variant::as_decimal(int fallback) const
310 {
311  if(is_decimal()) {
312  return value_cast<variant_decimal>()->get_numeric_value();
313  } else if(is_int()) {
314  return value_cast<variant_int>()->get_numeric_value() * 1000;
315  } else if(is_null()) {
316  return fallback;
317  }
318 
319  throw type_error(was_expecting("an integer or a decimal", *this));
320 }
321 
322 bool variant::as_bool() const
323 {
324  return value_->as_bool();
325 }
326 
327 const std::string& variant::as_string() const
328 {
329  must_be(formula_variant::type::string);
330  return value_cast<variant_string>()->get_string();
331 }
332 
333 const std::vector<variant>& variant::as_list() const
334 {
335  must_be(formula_variant::type::list);
336  return value_cast<variant_list>()->get_container();
337 }
338 
339 const std::map<variant, variant>& variant::as_map() const
340 {
341  must_be(formula_variant::type::map);
342  return value_cast<variant_map>()->get_container();
343 }
344 
346 {
347  if(is_list() && v.is_list()) {
348  auto& list = value_cast<variant_list>()->get_container();
349  auto& other_list = v.value_cast<variant_list>()->get_container();
350 
351  std::vector<variant> res;
352  res.reserve(list.size() + other_list.size());
353 
354  for(const auto& member : list) {
355  res.push_back(member);
356  }
357 
358  for(const auto& member : other_list) {
359  res.push_back(member);
360  }
361 
362  return variant(std::move(res));
363  }
364 
365  if(is_map() && v.is_map()) {
366  std::map<variant, variant> res = value_cast<variant_map>()->get_container();
367 
368  for(const auto& member : v.value_cast<variant_map>()->get_container()) {
369  res[member.first] = member.second;
370  }
371 
372  return variant(std::move(res));
373  }
374 
375  if(is_decimal() || v.is_decimal()) {
376  return variant(as_decimal() + v.as_decimal() , DECIMAL_VARIANT);
377  }
378 
379  return variant(as_int() + v.as_int());
380 }
381 
383 {
384  if(is_decimal() || v.is_decimal()) {
385  return variant(as_decimal() - v.as_decimal() , DECIMAL_VARIANT);
386  }
387 
388  return variant(as_int() - v.as_int());
389 }
390 
392 {
393  if(is_decimal() || v.is_decimal()) {
394 
395  long long long_int = as_decimal();
396 
397  long_int *= v.as_decimal();
398 
399  long_int /= 100;
400 
401  if(long_int%10 >= 5) {
402  long_int /= 10;
403  ++long_int;
404  } else {
405  long_int/=10;
406  }
407 
408  return variant(static_cast<int>(long_int) , DECIMAL_VARIANT );
409  }
410 
411  return variant(as_int() * v.as_int());
412 }
413 
415 {
416  if(is_decimal() || v.is_decimal()) {
417  int denominator = v.as_decimal();
418 
419  if(denominator == 0) {
420  throw type_error("decimal divide by zero error");
421  }
422 
423  long long long_int = as_decimal();
424 
425  long_int *= 10000;
426 
427  long_int /= denominator;
428 
429  if(long_int%10 >= 5) {
430  long_int /= 10;
431  ++long_int;
432  } else {
433  long_int/=10;
434  }
435 
436  return variant(static_cast<int>(long_int), DECIMAL_VARIANT);
437  }
438 
439  const int numerator = as_int();
440  const int denominator = v.as_int();
441 
442  if(denominator == 0) {
443  throw type_error("int divide by zero error");
444  }
445 
446  return variant(numerator / denominator);
447 }
448 
450 {
451  if(is_decimal() || v.is_decimal()) {
452  const int numerator = as_decimal();
453  const int denominator = v.as_decimal();
454  if(denominator == 0) {
455  throw type_error("divide by zero error");
456  }
457 
458  return variant(numerator % denominator, DECIMAL_VARIANT);
459  } else {
460  const int numerator = as_int();
461  const int denominator = v.as_int();
462  if(denominator == 0) {
463  throw type_error("divide by zero error");
464  }
465 
466  return variant(numerator % denominator);
467  }
468 }
469 
471 {
472  if(is_decimal() || v.is_decimal()) {
473 
474  double res = std::pow(as_decimal() / 1000.0 , v.as_decimal() / 1000.0);
475 
476  if(std::isnan(res)) {
477  return variant();
478  }
479 
480  return variant(res, DECIMAL_VARIANT);
481  }
482 
483  return variant(static_cast<int>(std::round(std::pow(static_cast<double>(as_int()), v.as_int()))));
484 }
485 
487 {
488  if(is_decimal()) {
489  return variant(-as_decimal(), DECIMAL_VARIANT);
490  }
491 
492  return variant(-as_int());
493 }
494 
495 bool variant::operator==(const variant& v) const
496 {
497  if(type() != v.type()) {
498  if(is_decimal() || v.is_decimal()) {
499  return as_decimal() == v.as_decimal();
500  }
501 
502  return false;
503  }
504 
505  return value_->equals(*v.value_);
506 }
507 
508 bool variant::operator!=(const variant& v) const
509 {
510  return !operator==(v);
511 }
512 
513 bool variant::operator<(const variant& v) const
514 {
515  if(type() != v.type()) {
516  if(is_decimal() && v.is_int()) {
517  return as_decimal() < v.as_decimal();
518  }
519 
520  if(v.is_decimal() && is_int()) {
521  return as_decimal() < v.as_decimal();
522  }
523 
524  return type() < v.type();
525  }
526 
527  return value_->less_than(*v.value_);
528 }
529 
530 bool variant::operator>=(const variant& v) const
531 {
532  return !(*this < v);
533 }
534 
535 bool variant::operator<=(const variant& v) const
536 {
537  return !(v < *this);
538 }
539 
540 bool variant::operator>(const variant& v) const
541 {
542  return v < *this;
543 }
544 
545 namespace implementation
546 {
547 /**
548  * Applies the provided function to the corresponding variants in both lists.
549  *
550  * @todo Expose this to the public API.
551  */
552 template<typename Func>
553 variant zip_transform(const variant& v1, const variant& v2, const Func& op_func)
554 {
555  const variant_vector& lhs = v1.as_list();
556  const variant_vector& rhs = v2.as_list();
557 
558  if(lhs.size() != rhs.size()) {
559  throw type_error("zip_transform requires two lists of the same length");
560  }
561 
562  std::vector<variant> res;
563  res.reserve(lhs.size());
564 
565  for(std::size_t i = 0; i < lhs.size(); ++i) {
566  res.push_back(std::invoke(op_func, lhs[i], rhs[i]));
567  }
568 
569  return variant(std::move(res));
570 }
571 
572 } // namespace implementation
573 
575 {
576  must_both_be(formula_variant::type::list, v);
577  return implementation::zip_transform(*this, v, std::plus<variant>{});
578 }
579 
581 {
582  must_both_be(formula_variant::type::list, v);
583  return implementation::zip_transform(*this, v, std::minus<variant>{});
584 }
585 
587 {
588  must_both_be(formula_variant::type::list, v);
589  return implementation::zip_transform(*this, v, std::multiplies<variant>{});
590 }
591 
593 {
594  must_both_be(formula_variant::type::list, v);
595  return implementation::zip_transform(*this, v, std::divides<variant>{});
596 }
597 
599 {
600  if(is_list()) {
601  v.must_be(formula_variant::type::list);
602 
603  std::vector<variant> res;
604  res.reserve(num_elements() + v.num_elements());
605 
606  for(std::size_t i = 0; i < num_elements(); ++i) {
607  res.push_back((*this)[i]);
608  }
609 
610  for(std::size_t i = 0; i < v.num_elements(); ++i) {
611  res.push_back(v[i]);
612  }
613 
614  return variant(std::move(res));
615  } else if(is_string()) {
616  v.must_be(formula_variant::type::string);
617  std::string res = as_string() + v.as_string();
618  return variant(res);
619  }
620 
621  throw type_error(was_expecting("a list or a string", *this));
622 }
623 
625 {
626  must_both_be(formula_variant::type::integer, v);
627 
628  return value_cast<variant_int>()->build_range_variant(v.as_int());
629 }
630 
631 bool variant::contains(const variant& v) const
632 {
633  if(!is_list() && !is_map()) {
634  throw type_error(was_expecting("a list or a map", *this));
635  }
636 
637  if(is_list()) {
638  return value_cast<variant_list>()->contains(v);
639  } else {
640  return value_cast<variant_map>()->contains(v);
641  }
642 }
643 
645 {
646  if(type() != t) {
648  }
649 }
650 
652 {
653  if(type() != t || second.type() != t) {
654  throw type_error(formatter() << "TYPE ERROR: expected two "
655  << variant_type_to_string(t) << " but found "
656  << type_string() << " (" << to_debug_string() << ")" << " and "
657  << second.type_string() << " (" << second.to_debug_string() << ")");
658  }
659 }
660 
661 std::string variant::serialize_to_string() const
662 {
663  return value_->get_serialized_string();
664 }
665 
666 void variant::serialize_from_string(const std::string& str)
667 {
668  try {
669  *this = formula(str).evaluate();
670  } catch(...) {
671  DBG_SF << "Evaluation failed with exception: " << utils::get_unknown_exception_type();
672  *this = variant(str);
673  }
674 }
675 
676 std::string variant::string_cast() const
677 {
678  return value_->string_cast();
679 }
680 
681 std::string variant::to_debug_string(bool verbose, formula_seen_stack* seen) const
682 {
683  if(!seen) {
684  formula_seen_stack seen_stack;
685  return value_->get_debug_string(seen_stack, verbose);
686  }
687 
688  return value_->get_debug_string(*seen, verbose);
689 }
690 
692 {
693  std::stack<variant> vars;
694  if(var.is_list()) {
695  for(std::size_t n = 1; n <= var.num_elements(); ++n) {
696  vars.push(var[var.num_elements() - n]);
697  }
698  } else {
699  vars.push(var);
700  }
701 
702  std::vector<variant> made_moves;
703 
704  while(!vars.empty()) {
705 
706  if(vars.top().is_null()) {
707  vars.pop();
708  continue;
709  }
710 
711  if(auto action = vars.top().try_convert<action_callable>()) {
712  variant res = action->execute_self(*this);
713  if(res.is_int() && res.as_bool()) {
714  made_moves.push_back(vars.top());
715  }
716  } else if(vars.top().is_string() && vars.top().as_string() == "continue") {
717 // if(infinite_loop_guardian_.continue_check()) {
718  made_moves.push_back(vars.top());
719 // } else {
720  //too many calls in a row - possible infinite loop
721 // ERR_SF << "ERROR #5001 while executing 'continue' formula keyword";
722 
723 // if(safe_call)
724 // error = variant(new game_logic::safe_call_result(nullptr, 5001));
725 // }
726  } else if(vars.top().is_string() && (vars.top().as_string() == "end_turn" || vars.top().as_string() == "end")) {
727  break;
728  } else {
729  //this information is unneeded when evaluating formulas from commandline
730  ERR_SF << "UNRECOGNIZED MOVE: " << vars.top().to_debug_string();
731  }
732 
733  vars.pop();
734  }
735 
736  return variant(std::move(made_moves));
737 }
738 
739 }
double t
Definition: astarsearch.cpp:63
std::unique_ptr< PangoAttribute, void(*)(PangoAttribute *)> value_
Definition: attributes.cpp:60
std::ostringstream wrapper.
Definition: formatter.hpp:40
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:48
Iterator class for the variant.
Definition: variant.hpp:196
bool operator!=(const variant_iterator &that) const
Definition: variant.cpp:134
const variant_value_base * container_
Definition: variant.hpp:228
variant operator*() const
Definition: variant.cpp:74
bool operator==(const variant_iterator &that) const
Definition: variant.cpp:121
variant_iterator & operator--()
Definition: variant.cpp:102
variant_iterator()
Constructor for a no-op iterator.
Definition: variant.cpp:60
variant_iterator & operator++()
Definition: variant.cpp:83
const variant_map_raw & get_container() const
Base class for all variant types.
virtual bool iterator_equals(const utils::any &, const utils::any &) const
Implements the equality functionality of variant_iterator for a value of this type.
virtual variant deref_iterator(const utils::any &iter) const
Implements the dereference functionality of variant_iterator for a value of this type.
virtual void iterator_dec(utils::any &) const
Implements the decrement functionality of variant_iterator for a value of this type.
virtual void iterator_inc(utils::any &) const
Implements the increment functionality of variant_iterator for a value of this type.
variant execute_variant(const variant &to_exec)
Definition: variant.cpp:691
value_base_ptr value_
Variant value.
Definition: variant.hpp:184
std::shared_ptr< T > value_cast() const
Definition: variant.hpp:166
variant operator-() const
Definition: variant.cpp:486
bool is_map() const
Definition: variant.hpp:70
bool is_list() const
Definition: variant.hpp:68
variant operator^(const variant &) const
Definition: variant.cpp:470
variant concatenate(const variant &v) const
Definition: variant.cpp:598
variant get_keys() const
Definition: variant.cpp:237
variant_iterator begin() const
Definition: variant.cpp:261
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition: variant.cpp:300
variant operator/(const variant &) const
Definition: variant.cpp:414
variant get_values() const
Definition: variant.cpp:249
void serialize_from_string(const std::string &str)
Definition: variant.cpp:666
int as_decimal(int fallback=0) const
Returns the variant's internal representation of decimal number: ie, 1.234 is represented as 1234.
Definition: variant.cpp:309
bool operator>=(const variant &) const
Definition: variant.cpp:530
void must_both_be(formula_variant::type t, const variant &second) const
Definition: variant.cpp:651
std::size_t num_elements() const
Definition: variant.cpp:276
DECIMAL_VARIANT_TYPE
Definition: variant.hpp:31
@ DECIMAL_VARIANT
Definition: variant.hpp:31
formula_variant::type type() const
Definition: variant.hpp:175
bool is_decimal() const
Definition: variant.hpp:66
bool operator!=(const variant &) const
Definition: variant.cpp:508
variant operator+(const variant &) const
Definition: variant.cpp:345
bool is_empty() const
Definition: variant.cpp:271
bool contains(const variant &other) const
Definition: variant.cpp:631
bool operator==(const variant &) const
Definition: variant.cpp:495
bool operator>(const variant &) const
Definition: variant.cpp:540
variant operator*(const variant &) const
Definition: variant.cpp:391
variant get_member(const std::string &name) const
Definition: variant.cpp:285
variant list_elements_mul(const variant &v) const
Definition: variant.cpp:586
std::string serialize_to_string() const
Definition: variant.cpp:661
variant list_elements_add(const variant &v) const
Definition: variant.cpp:574
const std::string & as_string() const
Definition: variant.cpp:327
std::string string_cast() const
Definition: variant.cpp:676
variant list_elements_div(const variant &v) const
Definition: variant.cpp:592
variant_iterator end() const
Definition: variant.cpp:266
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:322
bool operator<(const variant &) const
Definition: variant.cpp:513
variant operator[](std::size_t n) const
Definition: variant.cpp:189
bool is_string() const
Definition: variant.hpp:69
variant build_range(const variant &v) const
Definition: variant.cpp:624
std::string type_string() const
Gets string name of the current value type.
Definition: variant.hpp:157
bool is_callable() const
Definition: variant.hpp:67
variant operator%(const variant &) const
Definition: variant.cpp:449
const std::vector< variant > & as_list() const
Definition: variant.cpp:333
bool operator<=(const variant &) const
Definition: variant.cpp:535
void must_be(formula_variant::type t) const
Definition: variant.cpp:644
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:681
const std::map< variant, variant > & as_map() const
Definition: variant.cpp:339
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:64
bool is_int() const
Definition: variant.hpp:65
variant list_elements_sub(const variant &v) const
Definition: variant.cpp:580
std::size_t i
Definition: function.cpp:1031
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:296
Contains the implementation details for lexical_cast and shouldn't be used directly.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
variant zip_transform(const variant &v1, const variant &v2, const Func &op_func)
Applies the provided function to the corresponding variants in both lists.
Definition: variant.cpp:553
Definition: callable.hpp:26
std::shared_ptr< variant_value_base > value_base_ptr
static std::string variant_type_to_string(formula_variant::type type)
Definition: variant.cpp:39
std::vector< variant > variant_vector
std::vector< const_formula_callable_ptr > formula_seen_stack
static std::string was_expecting(const std::string &message, const variant &v)
Definition: variant.cpp:45
static value_base_ptr null_value(new variant_value_base)
std::string message
Definition: exceptions.hpp:30
The base template for associating string values with enum values.
Definition: enum_base.hpp:33
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static std::string get()
Definition: function.cpp:63
type_error(const std::string &str)
Definition: variant.cpp:55
static map_location::direction n
#define DBG_SF
Definition: variant.cpp:27
static lg::log_domain log_scripting_formula("scripting/formula")
#define ERR_SF
Definition: variant.cpp:30