1 /*
2    Copyright (C) 2008 - 2018 by David White <dave@whitevine.net>
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 #pragma once
16 
17 #include "formula/callable_fwd.hpp"
18 #include "formula/variant.hpp"
19 
20 #include <iostream>
21 #include <memory>
22 #include <set>
23 
24 namespace wfl
25 {
26 
27 // Interface for objects that can have formulae run on them
28 class formula_callable
29 {
30 public:
formula_callable(bool has_self=true)31 	explicit formula_callable(bool has_self = true) : type_(FORMULA_C), has_self_(has_self) {}
32 
~formula_callable()33 	virtual ~formula_callable() {
34 		for(auto& d : dtor_notify) {
35 			if(d) {
36 				d->notify_dead();
37 			}
38 		}
39 	}
40 
fake_ptr()41 	formula_callable_ptr fake_ptr() {
42 		return formula_callable_ptr(this, [](const formula_callable*){});
43 	}
44 
fake_ptr() const45 	const_formula_callable_ptr fake_ptr() const {
46 		return const_formula_callable_ptr(this, [](const formula_callable*){});
47 	}
48 
query_value(const std::string & key) const49 	variant query_value(const std::string& key) const
50 	{
51 		if(has_self_ && key == "self") {
52 			return variant(fake_ptr());
53 		}
54 		return get_value(key);
55 	}
56 
mutate_value(const std::string & key,const variant & value)57 	void mutate_value(const std::string& key, const variant& value)
58 	{
59 		set_value(key, value);
60 	}
61 
inputs() const62 	formula_input_vector inputs() const
63 	{
64 		formula_input_vector res;
65 		get_inputs(res);
66 		return res;
67 	}
68 
get_inputs(formula_input_vector &) const69 	virtual void get_inputs(formula_input_vector& /*inputs*/) const {}
70 
equals(const formula_callable & other) const71 	bool equals(const formula_callable& other) const
72 	{
73 		return do_compare(&other) == 0;
74 	}
75 
less(const formula_callable & other) const76 	bool less(const formula_callable& other) const
77 	{
78 		return do_compare(&other) < 0;
79 	}
80 
has_key(const std::string & key) const81 	bool has_key(const std::string& key) const
82 	{
83 		return !query_value(key).is_null();
84 	}
85 
86 	// Note: this function should NOT overwrite str, but append text to it!
87 	// TODO: return str instead of taking str.
serialize(std::string & str) const88 	void serialize(std::string& str) const
89 	{
90 		serialize_to_string(str);
91 	}
92 
subscribe_dtor(callable_die_subscriber * d) const93 	void subscribe_dtor(callable_die_subscriber* d) const {
94 		dtor_notify.insert(d);
95 	}
96 
unsubscribe_dtor(callable_die_subscriber * d) const97 	void unsubscribe_dtor(callable_die_subscriber* d) const {
98 		dtor_notify.erase(d);
99 	}
100 
101 protected:
102 	template<typename T, typename K>
convert_map(const std::map<T,K> & input_map)103 	static variant convert_map(const std::map<T, K>& input_map)
104 	{
105 		std::map<variant,variant> tmp;
106 		for(const auto& p : input_map) {
107 			tmp[variant(p.first)] = variant(p.second);
108 		}
109 
110 		return variant(tmp);
111 	}
112 
113 	template<typename T>
convert_set(const std::set<T> & input_set)114 	static variant convert_set(const std::set<T>& input_set)
115 	{
116 		std::map<variant,variant> tmp;
117 		for(const auto& elem : input_set) {
118 			tmp[variant(elem)] = variant(1);
119 		}
120 
121 		return variant(tmp);
122 	}
123 
124 	template<typename T>
convert_vector(const std::vector<T> & input_vector)125 	static variant convert_vector(const std::vector<T>& input_vector)
126 	{
127 		std::vector<variant> tmp;
128 		for(const auto& elem : input_vector) {
129 			tmp.emplace_back(elem);
130 		}
131 
132 		return variant(tmp);
133 	}
134 
add_input(formula_input_vector & inputs,const std::string & key,FORMULA_ACCESS_TYPE access_type=FORMULA_READ_ONLY)135 	static inline void add_input(formula_input_vector& inputs, const std::string& key, FORMULA_ACCESS_TYPE access_type = FORMULA_READ_ONLY)
136 	{
137 		inputs.emplace_back(key, access_type);
138 	}
139 
set_value(const std::string & key,const variant &)140 	virtual void set_value(const std::string& key, const variant& /*value*/)
141 	{
142 		std::cerr << "ERROR: cannot set key '" << key << "' on object" << std::endl;
143 	}
144 
do_compare(const formula_callable * callable) const145 	virtual int do_compare(const formula_callable* callable) const
146 	{
147 		if(type_ < callable->type_) {
148 			return -1;
149 		}
150 
151 		if(type_ > callable->type_) {
152 			return 1;
153 		}
154 
155 		return this < callable ? -1 : (this == callable ? 0 : 1);
156 	}
157 
158 	// Note: this function should NOT overwrite str, but append text to it!
159 	// TODO: return string not take string
serialize_to_string(std::string &) const160 	virtual void serialize_to_string(std::string& /*str*/) const
161 	{
162 		throw type_error("Tried to serialize type which cannot be serialized");
163 	}
164 
165 	// Priority for objects that are derived from this class, used in do_compare
166 	// when comparing objects of different types.
167 	// For example: formula_callable < terrain_callable < unit_type_callable ...
168 	enum TYPE {
169 		FORMULA_C,TERRAIN_C, LOCATION_C, UNIT_TYPE_C, UNIT_C,
170 		ATTACK_TYPE_C, MOVE_PARTIAL_C, MOVE_C, ATTACK_C, MOVE_MAP_C
171 	};
172 
173 	TYPE type_;
174 
175 	mutable std::set<callable_die_subscriber*> dtor_notify;
176 
177 private:
178 	virtual variant get_value(const std::string& key) const = 0;
179 	bool has_self_;
180 };
181 
182 class action_callable : public formula_callable
183 {
184 public:
185 	virtual variant execute_self(variant ctxt) = 0;
186 };
187 
188 class formula_callable_with_backup : public formula_callable
189 {
190 public:
formula_callable_with_backup(const formula_callable & main,const formula_callable & backup)191 	formula_callable_with_backup(const formula_callable& main, const formula_callable& backup)
192 		: formula_callable(false), main_(main), backup_(backup)
193 	{}
194 
195 private:
get_value(const std::string & key) const196 	variant get_value(const std::string& key) const override
197 	{
198 		variant var = main_.query_value(key);
199 		if(var.is_null()) {
200 			return backup_.query_value(key);
201 		}
202 
203 		return var;
204 	}
205 
get_inputs(formula_input_vector & inputs) const206 	void get_inputs(formula_input_vector& inputs) const override
207 	{
208 		main_.get_inputs(inputs);
209 		backup_.get_inputs(inputs);
210 	}
211 
212 	const formula_callable& main_;
213 	const formula_callable& backup_;
214 };
215 
216 class formula_variant_callable_with_backup : public formula_callable
217 {
218 public:
formula_variant_callable_with_backup(const variant & var,const formula_callable & backup)219 	formula_variant_callable_with_backup(const variant& var, const formula_callable& backup)
220 		: formula_callable(false), var_(var), backup_(backup)
221 	{}
222 
223 private:
get_value(const std::string & key) const224 	variant get_value(const std::string& key) const override
225 	{
226 		variant var = var_.get_member(key);
227 		if(var.is_null()) {
228 			return backup_.query_value(key);
229 		}
230 
231 		return var;
232 	}
233 
get_inputs(formula_input_vector & inputs) const234 	void get_inputs(formula_input_vector& inputs) const override
235 	{
236 		backup_.get_inputs(inputs);
237 	}
238 
239 	variant var_;
240 	const formula_callable& backup_;
241 };
242 
243 class map_formula_callable : public formula_callable
244 {
245 public:
map_formula_callable(const_formula_callable_ptr fallback=nullptr)246 	explicit map_formula_callable(const_formula_callable_ptr fallback = nullptr)
247 		: formula_callable(false)
248 		, values_()
249 		, fallback_(fallback)
250 	{}
251 
add(const std::string & key,const variant & value)252 	map_formula_callable& add(const std::string& key, const variant& value)
253 	{
254 		values_[key] = value;
255 		return *this;
256 	}
257 
set_fallback(const_formula_callable_ptr fallback)258 	void set_fallback(const_formula_callable_ptr fallback)
259 	{
260 		fallback_ = fallback;
261 	}
262 
empty() const263 	bool empty() const { return values_.empty(); }
264 
clear()265 	void clear() { values_.clear(); }
266 
267 	typedef std::map<std::string,variant>::const_iterator const_iterator;
268 
begin() const269 	const_iterator begin() const { return values_.begin(); }
end() const270 	const_iterator end()   const { return values_.end();   }
271 
272 private:
set_value(const std::string & key,const variant & value)273 	void set_value(const std::string& key, const variant& value) override
274 	{
275 		values_[key] = value;
276 	}
277 
get_value(const std::string & key) const278 	variant get_value(const std::string& key) const override
279 	{
280 		auto it = values_.find(key);
281 		if(it != values_.end()) {
282 			return it->second;
283 		} else if(fallback_) {
284 			return fallback_->query_value(key);
285 		}
286 
287 		return variant();
288 	}
289 
get_inputs(formula_input_vector & inputs) const290 	void get_inputs(formula_input_vector& inputs) const override
291 	{
292 		if(fallback_) {
293 			fallback_->get_inputs(inputs);
294 		}
295 
296 		for(const auto& i : values_) {
297 			add_input(inputs, i.first, FORMULA_READ_WRITE);
298 		}
299 	}
300 
301 	std::map<std::string, variant> values_;
302 	const_formula_callable_ptr fallback_;
303 };
304 
305 using map_formula_callable_ptr = std::shared_ptr<map_formula_callable>;
306 using const_map_formula_callable_ptr = std::shared_ptr<const map_formula_callable>;
307 
308 } // namespace wfl
309