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