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