The Battle for Wesnoth  1.19.2+dev
variant.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include <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 std::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  assert(value_.get());
148 }
149 
151  : value_(std::make_shared<variant_decimal>(n))
152 {
153  assert(value_.get());
154 }
155 
157  : value_(std::make_shared<variant_decimal>(n))
158 {
159  assert(value_.get());
160 }
161 
162 variant::variant(const std::vector<variant>& vec)
163  : value_((std::make_shared<variant_list>(vec)))
164 {
165  assert(value_.get());
166 }
167 
168 variant::variant(const std::string& str)
169  : value_(std::make_shared<variant_string>(str))
170 {
171  assert(value_.get());
172 }
173 
174 variant::variant(const std::map<variant,variant>& map)
175  : value_((std::make_shared<variant_map>(map)))
176 {
177  assert(value_.get());
178 }
179 
180 variant variant::operator[](std::size_t n) const
181 {
182  if(is_callable()) {
183  return *this;
184  }
185 
186  must_be(formula_variant::type::list);
187 
188  try {
189  return value_cast<variant_list>()->get_container().at(n);
190  } catch(std::out_of_range&) {
191  throw type_error("invalid index");
192  }
193 }
194 
196 {
197  if(is_callable()) {
198  return *this;
199  }
200 
201  if(is_map()) {
202  auto& map = value_cast<variant_map>()->get_container();
203 
204  auto i = map.find(v);
205  if(i == map.end()) {
206  return variant();
207  }
208 
209  return i->second;
210  } else if(is_list()) {
211  if(v.is_list()) {
212  std::vector<variant> slice;
213  for(std::size_t i = 0; i < v.num_elements(); ++i) {
214  slice.push_back((*this)[v[i]]);
215  }
216 
217  return variant(slice);
218  } else if(v.as_int() < 0) {
219  return operator[](num_elements() + v.as_int());
220  }
221 
222  return operator[](v.as_int());
223  }
224 
225  throw type_error(was_expecting("a list or a map", *this));
226 }
227 
229 {
230  must_be(formula_variant::type::map);
231 
232  std::vector<variant> tmp;
233  for(const auto& i : value_cast<variant_map>()->get_container()) {
234  tmp.push_back(i.first);
235  }
236 
237  return variant(tmp);
238 }
239 
241 {
242  must_be(formula_variant::type::map);
243 
244  std::vector<variant> tmp;
245  for(const auto& i : value_cast<variant_map>()->get_container()) {
246  tmp.push_back(i.second);
247  }
248 
249  return variant(tmp);
250 }
251 
253 {
254  return value_->make_iterator().begin();
255 }
256 
258 {
259  return value_->make_iterator().end();
260 }
261 
262 bool variant::is_empty() const
263 {
264  return value_->is_empty();
265 }
266 
267 std::size_t variant::num_elements() const
268 {
269  if(!is_list() && !is_map()) {
270  throw type_error(was_expecting("a list or a map", *this));
271  }
272 
273  return value_->num_elements();
274 }
275 
276 variant variant::get_member(const std::string& name) const
277 {
278  if(is_callable()) {
279  if(auto obj = value_cast<variant_callable>()->get_callable()) {
280  return obj->query_value(name);
281  }
282  }
283 
284  if(name == "self") {
285  return *this;
286  }
287 
288  return variant();
289 }
290 
291 int variant::as_int() const
292 {
293  if(is_null()) { return 0; }
294  if(is_decimal()) { return as_decimal() / 1000; }
295 
296  must_be(formula_variant::type::integer);
297  return value_cast<variant_int>()->get_numeric_value();
298 }
299 
301 {
302  if(is_decimal()) {
303  return value_cast<variant_decimal>()->get_numeric_value();
304  } else if(is_int()) {
305  return value_cast<variant_int>()->get_numeric_value() * 1000;
306  } else if(is_null()) {
307  return 0;
308  }
309 
310  throw type_error(was_expecting("an integer or a decimal", *this));
311 }
312 
313 bool variant::as_bool() const
314 {
315  return value_->as_bool();
316 }
317 
318 const std::string& variant::as_string() const
319 {
320  must_be(formula_variant::type::string);
321  return value_cast<variant_string>()->get_string();
322 }
323 
324 const std::vector<variant>& variant::as_list() const
325 {
326  must_be(formula_variant::type::list);
327  return value_cast<variant_list>()->get_container();
328 }
329 
330 const std::map<variant, variant>& variant::as_map() const
331 {
332  must_be(formula_variant::type::map);
333  return value_cast<variant_map>()->get_container();
334 }
335 
337 {
338  if(is_list() && v.is_list()) {
339  auto& list = value_cast<variant_list>()->get_container();
340  auto& other_list = v.value_cast<variant_list>()->get_container();
341 
342  std::vector<variant> res;
343  res.reserve(list.size() + other_list.size());
344 
345  for(const auto& member : list) {
346  res.push_back(member);
347  }
348 
349  for(const auto& member : other_list) {
350  res.push_back(member);
351  }
352 
353  return variant(res);
354  }
355 
356  if(is_map() && v.is_map()) {
357  std::map<variant, variant> res = value_cast<variant_map>()->get_container();
358 
359  for(const auto& member : v.value_cast<variant_map>()->get_container()) {
360  res[member.first] = member.second;
361  }
362 
363  return variant(res);
364  }
365 
366  if(is_decimal() || v.is_decimal()) {
367  return variant(as_decimal() + v.as_decimal() , DECIMAL_VARIANT);
368  }
369 
370  return variant(as_int() + v.as_int());
371 }
372 
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 
386  long long long_int = as_decimal();
387 
388  long_int *= v.as_decimal();
389 
390  long_int /= 100;
391 
392  if(long_int%10 >= 5) {
393  long_int /= 10;
394  ++long_int;
395  } else {
396  long_int/=10;
397  }
398 
399  return variant(static_cast<int>(long_int) , DECIMAL_VARIANT );
400  }
401 
402  return variant(as_int() * v.as_int());
403 }
404 
406 {
407  if(is_decimal() || v.is_decimal()) {
408  int denominator = v.as_decimal();
409 
410  if(denominator == 0) {
411  throw type_error("decimal divide by zero error");
412  }
413 
414  long long long_int = as_decimal();
415 
416  long_int *= 10000;
417 
418  long_int /= denominator;
419 
420  if(long_int%10 >= 5) {
421  long_int /= 10;
422  ++long_int;
423  } else {
424  long_int/=10;
425  }
426 
427  return variant(static_cast<int>(long_int), DECIMAL_VARIANT);
428  }
429 
430  const int numerator = as_int();
431  const int denominator = v.as_int();
432 
433  if(denominator == 0) {
434  throw type_error("int divide by zero error");
435  }
436 
437  return variant(numerator / denominator);
438 }
439 
441 {
442  if(is_decimal() || v.is_decimal()) {
443  const int numerator = as_decimal();
444  const int denominator = v.as_decimal();
445  if(denominator == 0) {
446  throw type_error("divide by zero error");
447  }
448 
449  return variant(numerator % denominator, DECIMAL_VARIANT);
450  } else {
451  const int numerator = as_int();
452  const int denominator = v.as_int();
453  if(denominator == 0) {
454  throw type_error("divide by zero error");
455  }
456 
457  return variant(numerator % denominator);
458  }
459 }
460 
462 {
463  if(is_decimal() || v.is_decimal()) {
464 
465  double res = std::pow(as_decimal() / 1000.0 , v.as_decimal() / 1000.0);
466 
467  if(std::isnan(res)) {
468  return variant();
469  }
470 
471  return variant(res, DECIMAL_VARIANT);
472  }
473 
474  return variant(static_cast<int>(std::round(std::pow(static_cast<double>(as_int()), v.as_int()))));
475 }
476 
478 {
479  if(is_decimal()) {
480  return variant(-as_decimal(), DECIMAL_VARIANT);
481  }
482 
483  return variant(-as_int());
484 }
485 
486 bool variant::operator==(const variant& v) const
487 {
488  if(type() != v.type()) {
489  if(is_decimal() || v.is_decimal()) {
490  return as_decimal() == v.as_decimal();
491  }
492 
493  return false;
494  }
495 
496  return value_->equals(*v.value_);
497 }
498 
499 bool variant::operator!=(const variant& v) const
500 {
501  return !operator==(v);
502 }
503 
504 bool variant::operator<(const variant& v) const
505 {
506  if(type() != v.type()) {
507  if(is_decimal() && v.is_int()) {
508  return as_decimal() < v.as_decimal();
509  }
510 
511  if(v.is_decimal() && is_int()) {
512  return as_decimal() < v.as_decimal();
513  }
514 
515  return type() < v.type();
516  }
517 
518  return value_->less_than(*v.value_);
519 }
520 
521 bool variant::operator>=(const variant& v) const
522 {
523  return !(*this < v);
524 }
525 
526 bool variant::operator<=(const variant& v) const
527 {
528  return !(v < *this);
529 }
530 
531 bool variant::operator>(const variant& v) const
532 {
533  return v < *this;
534 }
535 
537 {
538  must_both_be(formula_variant::type::list, v);
539  return value_cast<variant_list>()->list_op(v.value_, std::plus<variant>());
540 }
541 
543 {
544  must_both_be(formula_variant::type::list, v);
545  return value_cast<variant_list>()->list_op(v.value_, std::minus<variant>());
546 }
547 
549 {
550  must_both_be(formula_variant::type::list, v);
551  return value_cast<variant_list>()->list_op(v.value_, std::multiplies<variant>());
552 }
553 
555 {
556  must_both_be(formula_variant::type::list, v);
557  return value_cast<variant_list>()->list_op(v.value_, std::divides<variant>());
558 }
559 
561 {
562  if(is_list()) {
563  v.must_be(formula_variant::type::list);
564 
565  std::vector<variant> res;
566  res.reserve(num_elements() + v.num_elements());
567 
568  for(std::size_t i = 0; i < num_elements(); ++i) {
569  res.push_back((*this)[i]);
570  }
571 
572  for(std::size_t i = 0; i < v.num_elements(); ++i) {
573  res.push_back(v[i]);
574  }
575 
576  return variant(res);
577  } else if(is_string()) {
578  v.must_be(formula_variant::type::string);
579  std::string res = as_string() + v.as_string();
580  return variant(res);
581  }
582 
583  throw type_error(was_expecting("a list or a string", *this));
584 }
585 
587 {
588  must_both_be(formula_variant::type::integer, v);
589 
590  return value_cast<variant_int>()->build_range_variant(v.as_int());
591 }
592 
593 bool variant::contains(const variant& v) const
594 {
595  if(!is_list() && !is_map()) {
596  throw type_error(was_expecting("a list or a map", *this));
597  }
598 
599  if(is_list()) {
600  return value_cast<variant_list>()->contains(v);
601  } else {
602  return value_cast<variant_map>()->contains(v);
603  }
604 }
605 
607 {
608  if(type() != t) {
610  }
611 }
612 
614 {
615  if(type() != t || second.type() != t) {
616  throw type_error(formatter() << "TYPE ERROR: expected two "
617  << variant_type_to_string(t) << " but found "
618  << type_string() << " (" << to_debug_string() << ")" << " and "
619  << second.type_string() << " (" << second.to_debug_string() << ")");
620  }
621 }
622 
623 std::string variant::serialize_to_string() const
624 {
625  return value_->get_serialized_string();
626 }
627 
628 void variant::serialize_from_string(const std::string& str)
629 {
630  try {
631  *this = formula(str).evaluate();
632  } catch(...) {
633  DBG_SF << "Evaluation failed with exception: " << utils::get_unknown_exception_type();
634  *this = variant(str);
635  }
636 }
637 
638 std::string variant::string_cast() const
639 {
640  return value_->string_cast();
641 }
642 
643 std::string variant::to_debug_string(bool verbose, formula_seen_stack* seen) const
644 {
645  if(!seen) {
646  formula_seen_stack seen_stack;
647  return value_->get_debug_string(seen_stack, verbose);
648  }
649 
650  return value_->get_debug_string(*seen, verbose);
651 }
652 
654 {
655  std::stack<variant> vars;
656  if(var.is_list()) {
657  for(std::size_t n = 1; n <= var.num_elements(); ++n) {
658  vars.push(var[var.num_elements() - n]);
659  }
660  } else {
661  vars.push(var);
662  }
663 
664  std::vector<variant> made_moves;
665 
666  while(!vars.empty()) {
667 
668  if(vars.top().is_null()) {
669  vars.pop();
670  continue;
671  }
672 
673  if(auto action = vars.top().try_convert<action_callable>()) {
674  variant res = action->execute_self(*this);
675  if(res.is_int() && res.as_bool()) {
676  made_moves.push_back(vars.top());
677  }
678  } else if(vars.top().is_string() && vars.top().as_string() == "continue") {
679 // if(infinite_loop_guardian_.continue_check()) {
680  made_moves.push_back(vars.top());
681 // } else {
682  //too many calls in a row - possible infinite loop
683 // ERR_SF << "ERROR #5001 while executing 'continue' formula keyword";
684 
685 // if(safe_call)
686 // error = variant(new game_logic::safe_call_result(nullptr, 5001));
687 // }
688  } else if(vars.top().is_string() && (vars.top().as_string() == "end_turn" || vars.top().as_string() == "end")) {
689  break;
690  } else {
691  //this information is unneeded when evaluating formulas from commandline
692  ERR_SF << "UNRECOGNIZED MOVE: " << vars.top().to_debug_string();
693  }
694 
695  vars.pop();
696  }
697 
698  return variant(made_moves);
699 }
700 
701 }
double t
Definition: astarsearch.cpp:63
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:134
const variant_value_base * container_
Definition: variant.hpp:219
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
Base class for all variant types.
virtual bool iterator_equals(const std::any &, const std::any &) const
Implements the equality functionality of variant_iterator for a value of this type.
virtual void iterator_dec(std::any &) const
Implements the decrement functionality of variant_iterator for a value of this type.
virtual void iterator_inc(std::any &) const
Implements the increment functionality of variant_iterator for a value of this type.
virtual variant deref_iterator(const std::any &iter) const
Implements the dereference functionality of variant_iterator for a value of this type.
variant execute_variant(const variant &to_exec)
Definition: variant.cpp:653
value_base_ptr value_
Variant value.
Definition: variant.hpp:175
int as_int() const
Definition: variant.cpp:291
std::shared_ptr< T > value_cast() const
Definition: variant.hpp:157
variant operator-() const
Definition: variant.cpp:477
bool is_map() const
Definition: variant.hpp:68
bool is_list() const
Definition: variant.hpp:66
variant operator^(const variant &) const
Definition: variant.cpp:461
int as_decimal() const
Returns variant's internal representation of decimal number: ie, 1.234 is represented as 1234.
Definition: variant.cpp:300
variant concatenate(const variant &v) const
Definition: variant.cpp:560
variant get_keys() const
Definition: variant.cpp:228
variant_iterator begin() const
Definition: variant.cpp:252
variant operator/(const variant &) const
Definition: variant.cpp:405
variant get_values() const
Definition: variant.cpp:240
void serialize_from_string(const std::string &str)
Definition: variant.cpp:628
bool operator>=(const variant &) const
Definition: variant.cpp:521
void must_both_be(formula_variant::type t, const variant &second) const
Definition: variant.cpp:613
std::size_t num_elements() const
Definition: variant.cpp:267
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:499
variant operator+(const variant &) const
Definition: variant.cpp:336
bool is_empty() const
Definition: variant.cpp:262
bool contains(const variant &other) const
Definition: variant.cpp:593
bool operator==(const variant &) const
Definition: variant.cpp:486
bool operator>(const variant &) const
Definition: variant.cpp:531
variant operator*(const variant &) const
Definition: variant.cpp:382
variant get_member(const std::string &name) const
Definition: variant.cpp:276
variant list_elements_mul(const variant &v) const
Definition: variant.cpp:548
std::string serialize_to_string() const
Definition: variant.cpp:623
variant list_elements_add(const variant &v) const
Definition: variant.cpp:536
const std::string & as_string() const
Definition: variant.cpp:318
std::string string_cast() const
Definition: variant.cpp:638
variant list_elements_div(const variant &v) const
Definition: variant.cpp:554
variant_iterator end() const
Definition: variant.cpp:257
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
bool operator<(const variant &) const
Definition: variant.cpp:504
variant operator[](std::size_t n) const
Definition: variant.cpp:180
bool is_string() const
Definition: variant.hpp:67
variant build_range(const variant &v) const
Definition: variant.cpp:586
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:440
const std::vector< variant > & as_list() const
Definition: variant.cpp:324
bool operator<=(const variant &) const
Definition: variant.cpp:526
void must_be(formula_variant::type t) const
Definition: variant.cpp:606
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:643
const std::map< variant, variant > & as_map() const
Definition: variant.cpp:330
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:542
std::size_t i
Definition: function.cpp:968
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:298
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
Definition: contexts.hpp:43
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< 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:60
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