The Battle for Wesnoth  1.19.23+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 "utils/span.hpp"
21 
22 #include "formatter.hpp"
23 #include "formula/function.hpp"
25 #include "log.hpp"
26 
27 static lg::log_domain log_scripting_formula("scripting/formula");
28 #define DBG_SF LOG_STREAM(debug, log_scripting_formula)
29 #define LOG_SF LOG_STREAM(info, log_scripting_formula)
30 #define WRN_SF LOG_STREAM(warn, log_scripting_formula)
31 #define ERR_SF LOG_STREAM(err, log_scripting_formula)
32 
33 
34 namespace wfl
35 {
36 namespace
37 {
38 // Static value to initialize null variants to ensure its value is never nullptr.
39 const auto null_value = std::make_shared<variant_value_base>();
40 
41 std::string get_debug_description(const variant& v)
42 {
43  return formatter{} << formula_variant::get_string(v.type()) << " (" + v.to_debug_string() << ")";
44 }
45 
46 // Small helper function to get a standard type error message.
47 std::string was_expecting(const std::string& expected, const std::string& description)
48 {
49  return formatter{} << "TYPE ERROR: expected " << expected << " but found " << description;
50 }
51 
52 // Small helper function to get a standard type error message.
53 std::string was_expecting(const std::string& expected, const variant& v)
54 {
55  return was_expecting(expected, get_debug_description(v));
56 }
57 
58 // Throws a type_error if both variants are not of the given type
59 void must_both_be(formula_variant::type t, const variant& v1, const variant& v2)
60 {
61  if(v1.type() != t || v2.type() != t) {
62  const std::string expected = formatter{}
63  << formula_variant::get_string(t) << ", "
65 
66  const std::string provided = formatter{}
67  << get_debug_description(v1) << ", "
68  << get_debug_description(v2);
69 
70  throw type_error{was_expecting(expected, provided)};
71  }
72 }
73 
74 } // namespace
75 
76 template<typename T>
77 std::shared_ptr<T> value_cast(const variant& v)
78 {
79  auto res = std::dynamic_pointer_cast<T>(v.value_);
80  if(!res) {
81  throw type_error{was_expecting(formula_variant::get_string(T::value_type), v)};
82  }
83 
84  return res;
85 }
86 
87 type_error::type_error(const std::string& str) : game::error(str)
88 {
89  PLAIN_LOG << "ERROR: " << message << "\n" << call_stack_manager::get();
90 }
91 
93  : type_(formula_variant::type::null)
94  , container_(nullptr)
95  , iter_()
96 {
97 }
98 
99 variant_iterator::variant_iterator(const variant_value_base* value, const utils::any& iter)
100  : type_(value->get_type())
101  , container_(value)
102  , iter_(iter)
103 {
104 }
105 
107 {
108  if(!container_) {
109  return variant();
110  }
111 
113 }
114 
116 {
117  if(container_) {
119  }
120 
121  return *this;
122 }
123 
125 {
126  variant_iterator temp(*this);
127  if(container_) {
129  }
130 
131  return temp;
132 }
133 
135 {
136  if(container_) {
138  }
139 
140  return *this;
141 }
142 
144 {
145  variant_iterator temp(*this);
146  if(container_) {
148  }
149 
150  return temp;
151 }
152 
154 {
155  if(!container_ && !that.container_) {
156  return true;
157  }
158 
159  if(container_ == that.container_) {
160  return container_->iterator_equals(iter_, that.iter_);
161  }
162 
163  return false;
164 }
165 
167 {
168  return !operator==(that);
169 }
170 
171 
173  : value_(null_value)
174 {}
175 
177  : value_(std::make_shared<variant_int>(n))
178 {
179 }
180 
182  : value_(std::make_shared<variant_decimal>(n))
183 {
184 }
185 
187  : value_(std::make_shared<variant_decimal>(n))
188 {
189 }
190 
191 variant::variant(const std::vector<variant>& vec)
192  : value_(std::make_shared<variant_list>(vec))
193 {
194 }
195 
196 variant::variant(std::vector<variant>&& vec)
197  : value_(std::make_shared<variant_list>(std::move(vec)))
198 {
199 }
200 
201 variant::variant(const std::string& str)
202  : value_(std::make_shared<variant_string>(str))
203 {
204 }
205 
206 variant::variant(std::string&& str)
207  : value_(std::make_shared<variant_string>(std::move(str)))
208 {
209 }
210 
211 variant::variant(const std::map<variant, variant>& map)
212  : value_(std::make_shared<variant_map>(map))
213 {
214 }
215 
216 variant::variant(std::map<variant, variant>&& map)
217  : value_(std::make_shared<variant_map>(std::move(map)))
218 {
219 }
220 
222  : value_(std::make_shared<variant_callable>(std::move(callable)))
223 {
224 }
225 
227 {
228  return value_->get_type();
229 }
230 
231 variant variant::operator[](std::size_t n) const
232 {
233  if(is_callable()) {
234  return *this;
235  }
236 
237  try {
238  return as_list().at(n);
239  } catch(const std::out_of_range&) {
240  throw type_error("invalid index");
241  }
242 }
243 
245 {
246  if(is_callable()) {
247  return *this;
248  }
249 
250  if(is_map()) {
251  auto& map = as_map();
252 
253  auto i = map.find(v);
254  if(i == map.end()) {
255  return variant();
256  }
257 
258  return i->second;
259  } else if(is_list()) {
260  if(v.is_list()) {
261  std::vector<variant> slice;
262  for(std::size_t i = 0; i < v.num_elements(); ++i) {
263  slice.push_back((*this)[v[i]]);
264  }
265 
266  return variant(std::move(slice));
267  } else if(v.as_int() < 0) {
268  return operator[](num_elements() + v.as_int());
269  }
270 
271  return operator[](v.as_int());
272  }
273 
274  throw type_error(was_expecting("a list or a map", *this));
275 }
276 
278 {
279  std::vector<variant> tmp;
280  for(const auto& [key, value] : as_map()) {
281  tmp.push_back(key);
282  }
283 
284  return variant(std::move(tmp));
285 }
286 
288 {
289  std::vector<variant> tmp;
290  for(const auto& [key, value] : as_map()) {
291  tmp.push_back(value);
292  }
293 
294  return variant(std::move(tmp));
295 }
296 
298 {
299  return value_->make_iterator().begin();
300 }
301 
303 {
304  return value_->make_iterator().end();
305 }
306 
307 bool variant::is_empty() const
308 {
309  return value_->is_empty();
310 }
311 
312 std::size_t variant::num_elements() const
313 {
314  if(!is_list() && !is_map()) {
315  throw type_error(was_expecting("a list or a map", *this));
316  }
317 
318  return value_->num_elements();
319 }
320 
321 variant variant::get_member(const std::string& name) const
322 {
323  if(is_callable()) {
324  if(auto obj = as_callable()) {
325  return obj->query_value(name);
326  }
327  }
328 
329  if(name == "self") {
330  return *this;
331  }
332 
333  return variant();
334 }
335 
336 int variant::as_int(int fallback) const
337 {
338  if(is_null()) { return fallback; }
339  if(is_decimal()) { return as_decimal() / 1000; }
340 
341  return value_cast<variant_int>(*this)->get_numeric_value();
342 }
343 
344 int variant::as_decimal(int fallback) const
345 {
346  if(is_decimal()) {
347  return value_cast<variant_decimal>(*this)->get_numeric_value();
348  } else if(is_int()) {
349  return value_cast<variant_int>(*this)->get_numeric_value() * 1000;
350  } else if(is_null()) {
351  return fallback;
352  }
353 
354  throw type_error(was_expecting("an integer or a decimal", *this));
355 }
356 
357 bool variant::as_bool() const
358 {
359  return value_->as_bool();
360 }
361 
362 const std::string& variant::as_string() const
363 {
364  return value_cast<variant_string>(*this)->get_string();
365 }
366 
367 const std::vector<variant>& variant::as_list() const
368 {
369  return value_cast<variant_list>(*this)->get_container();
370 }
371 
372 const std::map<variant, variant>& variant::as_map() const
373 {
374  return value_cast<variant_map>(*this)->get_container();
375 }
376 
378 {
379  return value_cast<variant_callable>(*this)->get_callable();
380 }
381 
383 {
384  if(is_list() && v.is_list()) {
385  return concatenate(v);
386  }
387 
388  if(is_map() && v.is_map()) {
389  std::map<variant, variant> res = as_map();
390 
391  for(const auto& member : v.as_map()) {
392  res[member.first] = member.second;
393  }
394 
395  return variant(std::move(res));
396  }
397 
398  if(is_decimal() || v.is_decimal()) {
399  return variant(as_decimal() + v.as_decimal() , DECIMAL_VARIANT);
400  }
401 
402  return variant(as_int() + v.as_int());
403 }
404 
406 {
407  if(is_decimal() || v.is_decimal()) {
408  return variant(as_decimal() - v.as_decimal() , DECIMAL_VARIANT);
409  }
410 
411  return variant(as_int() - v.as_int());
412 }
413 
415 {
416  if(is_decimal() || v.is_decimal()) {
417 
418  long long long_int = as_decimal();
419 
420  long_int *= v.as_decimal();
421 
422  long_int /= 100;
423 
424  if(long_int%10 >= 5) {
425  long_int /= 10;
426  ++long_int;
427  } else {
428  long_int/=10;
429  }
430 
431  return variant(static_cast<int>(long_int) , DECIMAL_VARIANT );
432  }
433 
434  return variant(as_int() * v.as_int());
435 }
436 
438 {
439  if(is_decimal() || v.is_decimal()) {
440  int denominator = v.as_decimal();
441 
442  if(denominator == 0) {
443  throw type_error("decimal divide by zero error");
444  }
445 
446  long long long_int = as_decimal();
447 
448  long_int *= 10000;
449 
450  long_int /= denominator;
451 
452  if(long_int%10 >= 5) {
453  long_int /= 10;
454  ++long_int;
455  } else {
456  long_int/=10;
457  }
458 
459  return variant(static_cast<int>(long_int), DECIMAL_VARIANT);
460  }
461 
462  const int numerator = as_int();
463  const int denominator = v.as_int();
464 
465  if(denominator == 0) {
466  throw type_error("int divide by zero error");
467  }
468 
469  return variant(numerator / denominator);
470 }
471 
473 {
474  if(is_decimal() || v.is_decimal()) {
475  const int numerator = as_decimal();
476  const int denominator = v.as_decimal();
477  if(denominator == 0) {
478  throw type_error("divide by zero error");
479  }
480 
481  return variant(numerator % denominator, DECIMAL_VARIANT);
482  } else {
483  const int numerator = as_int();
484  const int denominator = v.as_int();
485  if(denominator == 0) {
486  throw type_error("divide by zero error");
487  }
488 
489  return variant(numerator % denominator);
490  }
491 }
492 
494 {
495  if(is_decimal() || v.is_decimal()) {
496 
497  double res = std::pow(as_decimal() / 1000.0 , v.as_decimal() / 1000.0);
498 
499  if(std::isnan(res)) {
500  return variant();
501  }
502 
503  return variant(res, DECIMAL_VARIANT);
504  }
505 
506  return variant(static_cast<int>(std::round(std::pow(static_cast<double>(as_int()), v.as_int()))));
507 }
508 
510 {
511  if(is_decimal()) {
512  return variant(-as_decimal(), DECIMAL_VARIANT);
513  }
514 
515  return variant(-as_int());
516 }
517 
518 bool variant::operator==(const variant& v) const
519 {
520  if(type() != v.type()) {
521  if(is_decimal() || v.is_decimal()) {
522  return as_decimal() == v.as_decimal();
523  }
524 
525  return false;
526  }
527 
528  return value_->equals(*v.value_);
529 }
530 
531 bool variant::operator!=(const variant& v) const
532 {
533  return !operator==(v);
534 }
535 
536 bool variant::operator<(const variant& v) const
537 {
538  if(type() != v.type()) {
539  if(is_decimal() && v.is_int()) {
540  return as_decimal() < v.as_decimal();
541  }
542 
543  if(v.is_decimal() && is_int()) {
544  return as_decimal() < v.as_decimal();
545  }
546 
547  return type() < v.type();
548  }
549 
550  return value_->less_than(*v.value_);
551 }
552 
553 bool variant::operator>=(const variant& v) const
554 {
555  return !(*this < v);
556 }
557 
558 bool variant::operator<=(const variant& v) const
559 {
560  return !(v < *this);
561 }
562 
563 bool variant::operator>(const variant& v) const
564 {
565  return v < *this;
566 }
567 
568 namespace implementation
569 {
570 /**
571  * Applies the provided function to the corresponding variants in both lists.
572  *
573  * @todo Expose this to the public API.
574  */
575 template<typename Func>
576 variant zip_transform(const variant& v1, const variant& v2, const Func& op_func)
577 {
578  must_both_be(formula_variant::type::list, v1, v2);
579 
580  const std::vector<variant>& lhs = v1.as_list();
581  const std::vector<variant>& rhs = v2.as_list();
582 
583  if(lhs.size() != rhs.size()) {
584  throw type_error("zip_transform requires two lists of the same length");
585  }
586 
587  std::vector<variant> res;
588  res.reserve(lhs.size());
589 
590  for(std::size_t i = 0; i < lhs.size(); ++i) {
591  res.push_back(std::invoke(op_func, lhs[i], rhs[i]));
592  }
593 
594  return variant(std::move(res));
595 }
596 
597 variant concat_lists(const variant& v1, const variant& v2)
598 {
599  must_both_be(formula_variant::type::list, v1, v2);
600 
601  const std::vector<variant>& lhs = v1.as_list();
602  const std::vector<variant>& rhs = v2.as_list();
603 
604  std::vector<variant> res;
605  res.reserve(lhs.size() + rhs.size());
606 
607  std::copy(lhs.begin(), lhs.end(), std::back_inserter(res));
608  std::copy(rhs.begin(), rhs.end(), std::back_inserter(res));
609 
610  return variant(std::move(res));
611 }
612 
613 } // namespace implementation
614 
616 {
617  return implementation::zip_transform(*this, v, std::plus<variant>{});
618 }
619 
621 {
622  return implementation::zip_transform(*this, v, std::minus<variant>{});
623 }
624 
626 {
627  return implementation::zip_transform(*this, v, std::multiplies<variant>{});
628 }
629 
631 {
632  return implementation::zip_transform(*this, v, std::divides<variant>{});
633 }
634 
636 {
637  if(is_list() && v.is_list()) {
638  return implementation::concat_lists(*this, v);
639  }
640 
641  if(is_string() && v.is_string()) {
642  return variant(as_string() + v.as_string());
643  }
644 
645  throw type_error(was_expecting("a list or a string", *this));
646 }
647 
649 {
650  must_both_be(formula_variant::type::integer, *this, v);
651  return value_cast<variant_int>(*this)->build_range_variant(v.as_int());
652 }
653 
654 bool variant::contains(const variant& v) const
655 {
656  switch(type()) {
657  case formula_variant::type::list:
658  return utils::contains(as_list(), v);
659  case formula_variant::type::map:
660  return utils::contains(as_map(), v);
661  default:
662  throw type_error(was_expecting("a list or a map", *this));
663  }
664 }
665 
666 std::string variant::serialize_to_string() const
667 {
668  return value_->get_serialized_string();
669 }
670 
671 void variant::serialize_from_string(const std::string& str)
672 {
673  try {
674  *this = formula(str).evaluate();
675  } catch(...) {
676  DBG_SF << "Evaluation failed with exception: " << utils::get_unknown_exception_type();
677  *this = variant(str);
678  }
679 }
680 
681 std::string variant::string_cast() const
682 {
683  return value_->string_cast();
684 }
685 
686 std::string variant::to_debug_string(bool verbose, formula_seen_stack* seen) const
687 {
688  if(!seen) {
689  formula_seen_stack seen_stack;
690  return value_->get_debug_string(seen_stack, verbose);
691  }
692 
693  return value_->get_debug_string(*seen, verbose);
694 }
695 
696 variant execute_actions(const variant& execute, const variant& context)
697 {
698  // If we don't have a list, try and execute the input variant itself.
699  const auto to_execute = execute.is_list()
700  ? utils::span{execute.as_list()}
701  : utils::span{&execute, 1};
702 
703  std::vector<variant> res;
704  res.reserve(to_execute.size());
705 
706  for(const variant& v : to_execute) {
707  if(v.is_null()) {
708  continue;
709  }
710 
711  auto action = callable_cast<action_callable*>(v);
712  if(!action) {
713  WRN_SF << "Could not execute non-action_callable variant: " << v.to_debug_string();
714  continue;
715  }
716 
717  res.push_back(std::const_pointer_cast<action_callable>(action)->execute_self(context));
718  }
719 
720  return variant(std::move(res));
721 }
722 
723 }
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:160
bool operator!=(const variant_iterator &that) const
Definition: variant.cpp:166
const variant_value_base * container_
Definition: variant.hpp:192
variant operator*() const
Definition: variant.cpp:106
bool operator==(const variant_iterator &that) const
Definition: variant.cpp:153
variant_iterator & operator--()
Definition: variant.cpp:134
variant_iterator()
Constructor for a no-op iterator.
Definition: variant.cpp:92
variant_iterator & operator++()
Definition: variant.cpp:115
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 operator-() const
Definition: variant.cpp:509
bool is_map() const
Definition: variant.hpp:75
bool is_list() const
Definition: variant.hpp:73
variant operator^(const variant &) const
Definition: variant.cpp:493
variant concatenate(const variant &v) const
Definition: variant.cpp:635
variant get_keys() const
Definition: variant.cpp:277
variant_iterator begin() const
Definition: variant.cpp:297
std::shared_ptr< variant_value_base > value_
Definition: variant.hpp:143
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition: variant.cpp:336
variant operator/(const variant &) const
Definition: variant.cpp:437
variant get_values() const
Definition: variant.cpp:287
void serialize_from_string(const std::string &str)
Definition: variant.cpp:671
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:344
const_formula_callable_ptr as_callable() const
Definition: variant.cpp:377
bool operator>=(const variant &) const
Definition: variant.cpp:553
std::size_t num_elements() const
Definition: variant.cpp:312
DECIMAL_VARIANT_TYPE
Definition: variant.hpp:41
@ DECIMAL_VARIANT
Definition: variant.hpp:41
formula_variant::type type() const
Definition: variant.cpp:226
bool is_decimal() const
Definition: variant.hpp:71
bool operator!=(const variant &) const
Definition: variant.cpp:531
variant operator+(const variant &) const
Definition: variant.cpp:382
bool is_empty() const
Definition: variant.cpp:307
bool contains(const variant &other) const
Definition: variant.cpp:654
bool operator==(const variant &) const
Definition: variant.cpp:518
bool operator>(const variant &) const
Definition: variant.cpp:563
variant operator*(const variant &) const
Definition: variant.cpp:414
variant get_member(const std::string &name) const
Definition: variant.cpp:321
variant list_elements_mul(const variant &v) const
Definition: variant.cpp:625
std::string serialize_to_string() const
Definition: variant.cpp:666
variant list_elements_add(const variant &v) const
Definition: variant.cpp:615
const std::string & as_string() const
Definition: variant.cpp:362
std::string string_cast() const
Definition: variant.cpp:681
variant list_elements_div(const variant &v) const
Definition: variant.cpp:630
variant_iterator end() const
Definition: variant.cpp:302
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:357
bool operator<(const variant &) const
Definition: variant.cpp:536
variant operator[](std::size_t n) const
Definition: variant.cpp:231
bool is_string() const
Definition: variant.hpp:74
variant build_range(const variant &v) const
Definition: variant.cpp:648
bool is_callable() const
Definition: variant.hpp:72
variant operator%(const variant &) const
Definition: variant.cpp:472
const std::vector< variant > & as_list() const
Definition: variant.cpp:367
bool operator<=(const variant &) const
Definition: variant.cpp:558
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:686
const std::map< variant, variant > & as_map() const
Definition: variant.cpp:372
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:69
bool is_int() const
Definition: variant.hpp:70
variant list_elements_sub(const variant &v) const
Definition: variant.cpp:620
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.
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:87
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:576
variant concat_lists(const variant &v1, const variant &v2)
Definition: variant.cpp:597
Definition: callable.hpp:26
std::vector< const_formula_callable_ptr > formula_seen_stack
std::shared_ptr< const formula_callable > const_formula_callable_ptr
std::shared_ptr< T > value_cast(const variant &v)
Definition: variant.cpp:77
variant execute_actions(const variant &execute, const variant &context)
Executes all action_callables in execute using the provided context.
Definition: variant.cpp:696
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:87
static map_location::direction n
#define WRN_SF
Definition: variant.cpp:30
#define DBG_SF
Definition: variant.cpp:28
static lg::log_domain log_scripting_formula("scripting/formula")