The Battle for Wesnoth  1.19.23+dev
variant_value.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2025
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "formula/variant.hpp"
17 
18 #include "utils/ranges.hpp"
19 #include <utility>
20 
21 #include "formula/callable.hpp"
22 #include "formula/function.hpp"
24 
25 namespace wfl
26 {
27 namespace implementation
28 {
29 template<typename Range>
30 auto make_iterator_range(const variant_value_base* val, const Range& range) -> boost::iterator_range<variant_iterator>
31 {
32  return {
33  variant_iterator{val, std::cbegin(range)},
34  variant_iterator{val, std::cend(range)}
35  };
36 }
37 
38 } // namespace implementation
39 
40 boost::iterator_range<variant_iterator> variant_value_base::make_iterator() const
41 {
42  return {variant_iterator(), variant_iterator()};
43 }
44 
45 variant variant_value_base::deref_iterator(const utils::any& /*iter*/) const
46 {
47  return variant();
48 }
49 
51 {
52  const int len = std::abs(limit - value_) + 1;
53 
54  std::vector<variant> res;
55  res.reserve(len);
56 
57  for(int i = value_; res.size() != res.capacity(); value_ < limit ? ++i : --i) {
58  res.emplace_back(i);
59  }
60 
61  return variant(std::move(res));
62 }
63 
64 std::string variant_decimal::to_string_impl(const bool sign_value) const
65 {
66  std::ostringstream ss;
67 
68  int fractional = value_ % 1000;
69  int integer = (value_ - fractional) / 1000;
70 
71  if(sign_value) {
72  // Make sure we get the sign on small negative values.
73  if(integer == 0 && value_ < 0) {
74  ss << '-';
75  }
76  }
77 
78  ss << integer << ".";
79 
80  fractional = std::abs(fractional);
81 
82  if(fractional < 100) {
83  if(fractional < 10) {
84  ss << "00";
85  } else {
86  ss << 0;
87  }
88  }
89 
90  ss << fractional;
91 
92  return ss.str();
93 }
94 
96  : callable_(std::move(callable))
97 {
98  if(callable_) {
99  callable_->subscribe_dtor(this);
100  }
101 }
102 
104 {
105  if(callable_) {
106  callable_->unsubscribe_dtor(this);
107  }
108 }
109 
111 {
112  if(callable_) {
113  return callable_->serialize();
114  }
115 
116  return {};
117 }
118 
119 std::string variant_callable::get_debug_string(formula_seen_stack& seen, bool verbose) const
120 {
121  std::ostringstream ss;
122  ss << "{";
123 
124  if(!callable_) {
125  ss << "null";
126  } else if(!utils::contains(seen, callable_)) {
127  if(!verbose) {
128  seen.push_back(callable_);
129  }
130 
131  formula_input_vector v = callable_->inputs();
132  bool first = true;
133 
134  for(const auto& input : v) {
135  if(!first) {
136  ss << ", ";
137  }
138 
139  first = false;
140  ss << input.name << " ";
141 
142  if(input.access == formula_access::read_write) {
143  ss << "(read-write) ";
144  } else if(input.access == formula_access::write_only) {
145  ss << "(writeonly) ";
146  }
147 
148  ss << "-> " << callable_->query_value(input.name).to_debug_string(verbose, &seen);
149  }
150  } else {
151  ss << "...";
152  }
153 
154  ss << "}";
155 
156  return ss.str();
157 }
158 
160 {
161  const variant_callable& other_ref = utils::cast_as(*this, other);
162  return callable_ ? callable_->equals(*other_ref.callable_) : callable_ == other_ref.callable_;
163 }
164 
166 {
167  const variant_callable& other_ref = utils::cast_as(*this, other);
168  return callable_ ? callable_->less(*other_ref.callable_) : other_ref.callable_ != nullptr;
169 }
170 
171 boost::iterator_range<variant_iterator> variant_callable::make_iterator() const
172 {
173  if(!callable_) {
175  }
176 
177  if(inputs.empty()) {
178  callable_->get_inputs(inputs);
179  }
180 
182 }
183 
184 variant variant_callable::deref_iterator(const utils::any& iter) const
185 {
186  if(!callable_) {
187  return variant();
188  }
189 
190  return callable_->query_value(utils::any_cast<const formula_input_vector::const_iterator&>(iter)->name);
191 }
192 
193 void variant_callable::iterator_inc(utils::any& iter) const
194 {
195  ++utils::any_cast<formula_input_vector::const_iterator&>(iter);
196 }
197 
198 void variant_callable::iterator_dec(utils::any& iter) const
199 {
200  --utils::any_cast<formula_input_vector::const_iterator&>(iter);
201 }
202 
204 {
205  std::ostringstream ss;
206  ss << "'";
207 
208  for(const auto& c : string_) {
209  switch(c) {
210  case '\'':
211  ss << "[']";
212  break;
213  case '[':
214  ss << "[(]";
215  break;
216  case ']':
217  ss << "[)]";
218  break;
219  default:
220  ss << c;
221  break;
222  }
223  }
224 
225  ss << "'";
226 
227  return ss.str();
228 }
229 
230 namespace implementation
231 {
232 namespace detail
233 {
234 template<typename Func>
235 std::string serialize_value(const Func& op, const variant& value)
236 {
237  return std::invoke(op, value);
238 }
239 
240 template<typename Func>
241 std::string serialize_value(const Func& op, const std::pair<variant, variant>& value)
242 {
243  std::ostringstream ss;
244 
245  ss << std::invoke(op, value.first);
246  ss << "->";
247  ss << std::invoke(op, value.second);
248 
249  return ss.str();
250 }
251 
252 template<typename Range, typename Func>
253 auto make_serialized_range(const Range& range, const Func& op)
254 {
255  return range | utils::views::transform([&op](auto&& value) { return serialize_value(op, value); });
256 }
257 
258 /** WFL empty list literal */
259 std::string serialize_empty(const std::vector<variant>&)
260 {
261  return "[]";
262 }
263 
264 /** WFL empty map literal */
265 std::string serialize_empty(const std::map<variant, variant>&)
266 {
267  return "[->]";
268 }
269 
270 } // namespace detail
271 
272 template<typename Range, typename Func>
273 std::string to_string(const Range& range, const Func& op)
274 {
275  return utils::join(detail::make_serialized_range(range, op), ", ");
276 }
277 
278 template<typename Range, typename Func>
279 std::string as_literal(const Range& range, const Func& op)
280 {
281  if(range.empty()) {
282  return detail::serialize_empty(range);
283  }
284 
285  std::ostringstream ss;
286  ss << "[" << to_string(range, op) << "]";
287  return ss.str();
288 }
289 
290 } // namespace implementation
291 
292 template<typename T>
294 {
295  return implementation::to_string(container_, &variant::string_cast);
296 }
297 
298 template<typename T>
300 {
302 }
303 
304 template<typename T>
305 std::string variant_container<T>::get_debug_string(formula_seen_stack& seen, bool verbose) const
306 {
307  return implementation::as_literal(container_,
308  [&seen, verbose](const variant& v) { return v.to_debug_string(verbose, &seen); });
309 }
310 
311 template<typename T>
312 boost::iterator_range<variant_iterator> variant_container<T>::make_iterator() const
313 {
314  return implementation::make_iterator_range(this, container_);
315 }
316 
317 template<typename T>
318 void variant_container<T>::iterator_inc(utils::any& iter) const
319 {
320  ++as_container_iterator(iter);
321 }
322 
323 template<typename T>
324 void variant_container<T>::iterator_dec(utils::any& iter) const
325 {
326  --as_container_iterator(iter);
327 }
328 
329 template<typename T>
330 bool variant_container<T>::iterator_equals(const utils::any& first, const utils::any& second) const
331 {
332  return as_container_iterator(first) == as_container_iterator(second);
333 }
334 
335 // Force compilation of the following template instantiations
338 
339 variant variant_list::deref_iterator(const utils::any& iter) const
340 {
341  return *as_container_iterator(iter);
342 }
343 
344 variant variant_map::deref_iterator(const utils::any& iter) const
345 {
346  const auto& [key, value] = *as_container_iterator(iter);
347  return make_callable<key_value_pair>(key, value);
348 }
349 
350 } // namespace wfl
virtual bool equals(const variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
virtual std::string get_debug_string(formula_seen_stack &seen, bool verbose) const override
Returns debug info for the variant value.
variant_callable(const_formula_callable_ptr callable)
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
virtual variant deref_iterator(const utils::any &iter) const override
Implements the dereference functionality of variant_iterator for a value of this type.
virtual void iterator_inc(utils::any &iter) const override
Implements the increment functionality of variant_iterator for a value of this type.
virtual boost::iterator_range< variant_iterator > make_iterator() const override
Required by variant_value_base.
formula_input_vector inputs
virtual void iterator_dec(utils::any &iter) const override
Implements the decrement functionality of variant_iterator for a value of this type.
const_formula_callable_ptr callable_
virtual bool less_than(const variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
Generalized interface for container variants.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
virtual void iterator_dec(utils::any &) const override
Implements the decrement functionality of variant_iterator for a value of this type.
virtual std::string get_debug_string(formula_seen_stack &seen, bool verbose) const override
Returns debug info for the variant value.
virtual boost::iterator_range< variant_iterator > make_iterator() const override
Creates an iterator pair that can be used for iteration.
static const_iterator & as_container_iterator(utils::any &iter)
Casts opaque iter to a mutable const_iterator reference.
virtual bool iterator_equals(const utils::any &first, const utils::any &second) const override
Implements the equality functionality of variant_iterator for a value of this type.
virtual void iterator_inc(utils::any &) const override
Implements the increment functionality of variant_iterator for a value of this type.
std::string to_string_impl(const bool sign_value) const
Required by variant_value_base.
variant build_range_variant(int limit) const
Iterator class for the variant.
Definition: variant.hpp:160
virtual variant deref_iterator(const utils::any &) const override
Required by variant_value_base.
virtual variant deref_iterator(const utils::any &) const override
Required by variant_value_base.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
std::string string_
Required by variant_value_base.
Base class for all variant types.
virtual boost::iterator_range< variant_iterator > make_iterator() const
Creates an iterator pair that can be used for iteration.
virtual variant deref_iterator(const utils::any &iter) const
Implements the dereference functionality of variant_iterator for a value of this type.
std::string serialize_to_string() const
Definition: variant.cpp:666
std::string string_cast() const
Definition: variant.cpp:681
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:686
std::size_t i
Definition: function.cpp:1031
Contains the implementation details for lexical_cast and shouldn't be used directly.
constexpr auto transform
Definition: ranges.hpp:45
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:87
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
To & cast_as(To &, From &value)
auto make_serialized_range(const Range &range, const Func &op)
std::string serialize_empty(const std::vector< variant > &)
WFL empty list literal.
std::string serialize_value(const Func &op, const variant &value)
auto make_iterator_range(const variant_value_base *val, const Range &range) -> boost::iterator_range< variant_iterator >
std::string to_string(const Range &range, const Func &op)
std::string as_literal(const Range &range, const Func &op)
Definition: callable.hpp:26
std::vector< const_formula_callable_ptr > formula_seen_stack
std::vector< formula_input > formula_input_vector
std::shared_ptr< const formula_callable > const_formula_callable_ptr
mock_char c