Refactor variant class
This splits the handling of each applicable type into its own helper class to better encapsulate the functionality of each. It also contains a lot of related cleanup and improvements to the class interface itself.
This commit is contained in:
parent
d8c670654a
commit
99874f4253
22 changed files with 1202 additions and 915 deletions
|
@ -380,6 +380,8 @@
|
|||
<Unit filename="../../src/formula/tokenizer.hpp" />
|
||||
<Unit filename="../../src/formula/variant.cpp" />
|
||||
<Unit filename="../../src/formula/variant.hpp" />
|
||||
<Unit filename="../../src/formula/variant_private.cpp" />
|
||||
<Unit filename="../../src/formula/variant_private.hpp" />
|
||||
<Unit filename="../../src/game_board.cpp" />
|
||||
<Unit filename="../../src/game_board.hpp" />
|
||||
<Unit filename="../../src/game_classification.cpp" />
|
||||
|
|
|
@ -112,6 +112,7 @@ formula/function.cpp
|
|||
formula/string_utils.cpp
|
||||
formula/tokenizer.cpp
|
||||
formula/variant.cpp
|
||||
formula/variant_private.cpp
|
||||
game_board.cpp
|
||||
game_classification.cpp
|
||||
game_config_manager.cpp
|
||||
|
|
|
@ -344,7 +344,7 @@ public:
|
|||
for(attacks_vector::const_iterator i = value.begin(); i != value.end(); ++i) {
|
||||
vars.push_back(variant(new attack_analysis(*i)));
|
||||
}
|
||||
var = variant(&vars);
|
||||
var = variant(vars);
|
||||
}
|
||||
|
||||
static variant value_to_variant(const attacks_vector &value)
|
||||
|
|
|
@ -338,14 +338,14 @@ variant attack_analysis::get_value(const std::string& key) const
|
|||
res.push_back(variant(item));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
} else if(key == "units") {
|
||||
std::vector<variant> res;
|
||||
for(size_t n = 0; n != movements.size(); ++n) {
|
||||
res.push_back(variant(new location_callable(movements[n].first)));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
} else if(key == "target_value") {
|
||||
return variant(static_cast<int>(target_value*1000));
|
||||
} else if(key == "avg_losses") {
|
||||
|
|
|
@ -549,7 +549,7 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
|
|||
}
|
||||
}
|
||||
|
||||
return variant(&made_moves);
|
||||
return variant(made_moves);
|
||||
}
|
||||
|
||||
void formula_ai::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
|
||||
|
@ -570,7 +570,7 @@ variant villages_from_set(const Container& villages,
|
|||
vars.push_back(variant(new location_callable(loc)));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,7 +627,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
for(const std::string &i : rp) {
|
||||
vars.push_back(variant(i));
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "scout_village_targeting")
|
||||
{
|
||||
|
@ -671,7 +671,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
for(std::vector<team>::const_iterator i = resources::gameboard->teams().begin(); i != resources::gameboard->teams().end(); ++i) {
|
||||
vars.push_back(variant(new team_callable(*i)));
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "allies")
|
||||
{
|
||||
|
@ -680,7 +680,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
if ( !current_team().is_enemy( i+1 ) )
|
||||
vars.push_back(variant( i ));
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "enemies")
|
||||
{
|
||||
|
@ -689,7 +689,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
if ( current_team().is_enemy( i+1 ) )
|
||||
vars.push_back(variant( i ));
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "my_recruits")
|
||||
{
|
||||
|
@ -699,7 +699,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
|
||||
const std::set<std::string>& recruits = current_team().recruits();
|
||||
if(recruits.empty()) {
|
||||
return variant( &vars );
|
||||
return variant(vars);
|
||||
}
|
||||
for(std::set<std::string>::const_iterator i = recruits.begin(); i != recruits.end(); ++i)
|
||||
{
|
||||
|
@ -709,7 +709,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
vars.push_back(variant(new unit_type_callable(*ut)));
|
||||
}
|
||||
}
|
||||
return variant( &vars );
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "recruits_of_side")
|
||||
{
|
||||
|
@ -738,8 +738,8 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
}
|
||||
|
||||
for( size_t i = 0; i<tmp.size(); ++i)
|
||||
vars.push_back( variant( &tmp[i] ));
|
||||
return variant(&vars);
|
||||
vars.emplace_back(tmp[i]);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "units")
|
||||
{
|
||||
|
@ -747,7 +747,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
|
||||
vars.push_back(variant(new unit_callable(*i)));
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "units_of_side")
|
||||
{
|
||||
|
@ -762,8 +762,8 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
tmp[u.side() - 1].push_back(variant(new unit_callable(u)));
|
||||
}
|
||||
for( size_t i = 0; i<tmp.size(); ++i)
|
||||
vars.push_back( variant( &tmp[i] ));
|
||||
return variant(&vars);
|
||||
vars.emplace_back(tmp[i]);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "my_units")
|
||||
{
|
||||
|
@ -773,7 +773,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
vars.push_back(variant(new unit_callable(*i)));
|
||||
}
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "enemy_units")
|
||||
{
|
||||
|
@ -785,7 +785,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
}
|
||||
}
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "my_moves")
|
||||
{
|
||||
|
@ -814,7 +814,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
tmp.push_back( variant( new unit_callable(**i) ) );
|
||||
}
|
||||
|
||||
return variant( &tmp );
|
||||
return variant(tmp);
|
||||
|
||||
} else if(key == "vars")
|
||||
{
|
||||
|
@ -839,7 +839,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
{
|
||||
vars[i] = villages_from_set(resources::gameboard->teams()[i].villages());
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
|
||||
} else if(key == "my_villages")
|
||||
{
|
||||
|
@ -903,7 +903,7 @@ variant formula_ai::get_keeps() const
|
|||
}
|
||||
}
|
||||
}
|
||||
keeps_cache_ = variant(&vars);
|
||||
keeps_cache_ = variant(vars);
|
||||
}
|
||||
|
||||
return keeps_cache_;
|
||||
|
@ -1035,7 +1035,7 @@ config formula_ai::to_config() const
|
|||
for(game_logic::map_formula_callable::const_iterator i = vars_.begin(); i != vars_.end(); ++i)
|
||||
{
|
||||
try {
|
||||
i->second.serialize_to_string(str);
|
||||
str = i->second.serialize_to_string();
|
||||
} catch (type_error&) {
|
||||
WRN_AI << "variable ["<< i->first <<"] is not serializable - it will not be persisted across savegames"<<std::endl;
|
||||
continue;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "game_board.hpp"
|
||||
#include "ai/formula/callable_objects.hpp"
|
||||
#include "resources.hpp"
|
||||
|
||||
#include "map/map.hpp"
|
||||
|
||||
namespace game_logic {
|
||||
|
||||
|
@ -33,7 +33,7 @@ variant move_map_callable::get_value(const std::string& key) const
|
|||
}
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
} else if(key == "has_moves") {
|
||||
return variant(!srcdst_.empty());
|
||||
} else {
|
||||
|
@ -94,11 +94,11 @@ void position_callable::get_inputs(formula_input_vector* inputs) const {
|
|||
|
||||
variant outcome_callable::get_value(const std::string& key) const {
|
||||
if(key == "hitpoints_left") {
|
||||
return variant(new std::vector<variant>(hitLeft_));
|
||||
return variant(hitLeft_);
|
||||
} else if(key == "probability") {
|
||||
return variant(new std::vector<variant>(prob_));
|
||||
return variant(prob_);
|
||||
} else if(key == "possible_status") {
|
||||
return variant(new std::vector<variant>(status_));
|
||||
return variant(status_);
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ variant attack_map_callable::get_value(const std::string& key) const {
|
|||
collect_possible_attacks(vars, i->get_location(), i->get_location());
|
||||
}
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ void move_candidate_action::evaluate(ai::formula_ai* ai, unit_map& units)
|
|||
}
|
||||
}
|
||||
|
||||
variant my_units(&unit_vector);
|
||||
variant my_units(unit_vector);
|
||||
|
||||
variant filtered_units;
|
||||
try {
|
||||
|
@ -170,8 +170,8 @@ void attack_candidate_action::evaluate(ai::formula_ai* ai, unit_map& units)
|
|||
}
|
||||
}
|
||||
}
|
||||
variant my_units(&my_res);
|
||||
variant enemy_units(&enemy_res);
|
||||
variant my_units(my_res);
|
||||
variant enemy_units(enemy_res);
|
||||
|
||||
variant filtered_my_units, filtered_enemy_units;
|
||||
try {
|
||||
|
@ -203,7 +203,7 @@ void attack_candidate_action::evaluate(ai::formula_ai* ai, unit_map& units)
|
|||
std::vector< const unit_callable* > enemy_units_flt;
|
||||
|
||||
for(variant_iterator i = filtered_my_units.begin() ; i != filtered_my_units.end() ; ++i) {
|
||||
const unit_callable* u_callable = dynamic_cast<const unit_callable*>( (*i).as_callable() );
|
||||
const unit_callable* u_callable = dynamic_cast<const unit_callable*>( (*i).as_callable().get() );
|
||||
if(u_callable == nullptr) {
|
||||
ERR_AI << "ERROR in "<< get_name() << "Candidate Action: Filter formula returned table that does not contain units" << std::endl;
|
||||
return;
|
||||
|
@ -212,7 +212,7 @@ void attack_candidate_action::evaluate(ai::formula_ai* ai, unit_map& units)
|
|||
}
|
||||
|
||||
for(variant_iterator i = filtered_enemy_units.begin() ; i != filtered_enemy_units.end() ; ++i) {
|
||||
const unit_callable* u_callable = dynamic_cast<const unit_callable*>( (*i).as_callable() );
|
||||
const unit_callable* u_callable = dynamic_cast<const unit_callable*>( (*i).as_callable().get() );
|
||||
if(u_callable == nullptr) {
|
||||
ERR_AI << "ERROR in "<< get_name() << "Candidate Action: Filter formula returned table that does not contain units" << std::endl;
|
||||
return;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "display.hpp"
|
||||
#include "log.hpp"
|
||||
#include "map/label.hpp"
|
||||
#include "map/map.hpp"
|
||||
#include "pathfind/teleport.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "resources.hpp"
|
||||
|
@ -366,7 +367,7 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
|
@ -425,7 +426,7 @@ private:
|
|||
v.push_back(variant(new location_callable(adj[n])));
|
||||
}
|
||||
|
||||
return variant(&v);
|
||||
return variant(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -462,7 +463,7 @@ private:
|
|||
v.push_back(variant(new location_callable(res[n])));
|
||||
}
|
||||
|
||||
return variant(&v);
|
||||
return variant(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -550,7 +551,7 @@ private:
|
|||
res.push_back( variant(new location_callable( ml ) ) );
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -685,7 +686,7 @@ private:
|
|||
vars.push_back(variant(new location_callable(map_location(i, j))));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
|
@ -718,7 +719,7 @@ private:
|
|||
}
|
||||
++un;
|
||||
}
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
|
@ -803,7 +804,7 @@ private:
|
|||
if (bc.get_attacker_stats().plagues && hitLeft[0].as_int() == 0)
|
||||
status.push_back(variant("Zombiefied"));
|
||||
vars.push_back(variant(new outcome_callable(hitLeft, prob, status)));
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -838,7 +839,7 @@ private:
|
|||
vars.push_back(variant(new position_callable(/*&units,*/ static_cast<int>(100 - analysis->chance_to_kill*100))));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -949,7 +950,7 @@ private:
|
|||
map_location unit_loc;
|
||||
|
||||
if( src == dst )
|
||||
return variant(&locations);
|
||||
return variant(locations);
|
||||
|
||||
if(args().size() > 2)
|
||||
unit_loc = convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"shortest_path:unit_location")))->loc();
|
||||
|
@ -969,14 +970,14 @@ private:
|
|||
pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
|
||||
|
||||
if( route.steps.size() < 2 ) {
|
||||
return variant(&locations);
|
||||
return variant(locations);
|
||||
}
|
||||
|
||||
for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
|
||||
locations.push_back( variant( new location_callable(*loc_iter) ));
|
||||
}
|
||||
|
||||
return variant(&locations);
|
||||
return variant(locations);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
|
@ -999,7 +1000,7 @@ private:
|
|||
map_location unit_loc;
|
||||
|
||||
if( src == dst )
|
||||
return variant(&locations);
|
||||
return variant(locations);
|
||||
|
||||
if(args().size() > 2)
|
||||
unit_loc = convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"simplest_path:unit_location")))->loc();
|
||||
|
@ -1021,7 +1022,7 @@ private:
|
|||
pathfind::plain_route route = pathfind::a_star_search(src, dst, 1000.0, em_calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
|
||||
|
||||
if( route.steps.size() < 2 ) {
|
||||
return variant(&locations);
|
||||
return variant(locations);
|
||||
}
|
||||
|
||||
for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
|
||||
|
@ -1031,7 +1032,7 @@ private:
|
|||
break;
|
||||
}
|
||||
|
||||
return variant(&locations);
|
||||
return variant(locations);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
|
@ -1228,7 +1229,7 @@ private:
|
|||
std::vector<variant> res;
|
||||
res.push_back(var0);
|
||||
res.push_back(var1);
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
void display_label(const map_location& location, const std::string& text) const {
|
||||
|
@ -1331,7 +1332,7 @@ private:
|
|||
variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_moves:unit_location"));
|
||||
std::vector<variant> vars;
|
||||
if(res.is_null()) {
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
|
||||
const map_location& loc = convert_variant<location_callable>(res)->loc();
|
||||
|
@ -1343,7 +1344,7 @@ private:
|
|||
vars.push_back(variant(new location_callable(i->second)));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
|
@ -1369,7 +1370,7 @@ private:
|
|||
++range.first;
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1598,7 +1599,7 @@ private:
|
|||
vars.push_back(variant(best_defender_attacks.first));
|
||||
vars.push_back(variant(best_defender_attacks.second));
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ public:
|
|||
explicit formula_callable(bool has_self=true) : type_(FORMULA_C), has_self_(has_self)
|
||||
{}
|
||||
|
||||
virtual ~formula_callable() {}
|
||||
|
||||
variant query_value(const std::string& key) const {
|
||||
if(has_self_ && key == "self") {
|
||||
return variant(this);
|
||||
|
@ -84,7 +86,7 @@ public:
|
|||
tmp[variant(p.first)] = variant(p.second);
|
||||
}
|
||||
|
||||
return variant(&tmp);
|
||||
return variant(tmp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -96,7 +98,7 @@ public:
|
|||
tmp[variant(elem)] = variant(1);
|
||||
}
|
||||
|
||||
return variant(&tmp);
|
||||
return variant(tmp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -108,7 +110,7 @@ public:
|
|||
tmp.push_back(variant(elem));
|
||||
}
|
||||
|
||||
return variant(&tmp);
|
||||
return variant(tmp);
|
||||
}
|
||||
|
||||
static inline void add_input(formula_input_vector* inputs, const std::string& key, FORMULA_ACCESS_TYPE access_type = FORMULA_READ_ONLY)
|
||||
|
@ -117,8 +119,6 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
virtual ~formula_callable() {}
|
||||
|
||||
virtual void set_value(const std::string& key, const variant& value);
|
||||
virtual int do_compare(const formula_callable* callable) const {
|
||||
if( type_ < callable->type_ )
|
||||
|
|
|
@ -15,13 +15,17 @@
|
|||
#ifndef FORMULA_CALLABLE_FWD_HPP_INCLUDED
|
||||
#define FORMULA_CALLABLE_FWD_HPP_INCLUDED
|
||||
|
||||
namespace game_logic {
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace game_logic
|
||||
{
|
||||
class formula_callable;
|
||||
class formula_debugger;
|
||||
typedef std::shared_ptr<formula_callable> formula_callable_ptr;
|
||||
typedef std::shared_ptr<const formula_callable> const_formula_callable_ptr;
|
||||
|
||||
using formula_callable_ptr = std::shared_ptr<formula_callable>;
|
||||
using const_formula_callable_ptr = std::shared_ptr<const formula_callable>;
|
||||
using const_formula_callable_vec = std::vector<const formula_callable*>; // TODO: use shared_ptr
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "config.hpp"
|
||||
#include "formula/function.hpp"
|
||||
#include "map/map.hpp"
|
||||
#include "team.hpp"
|
||||
#include "units/formula_manager.hpp"
|
||||
|
||||
|
@ -91,7 +92,7 @@ variant attack_type_callable::get_value(const std::string& key) const
|
|||
res.push_back(variant(special.cfg["id"].str()));
|
||||
}
|
||||
}
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
return variant();
|
||||
|
@ -177,7 +178,7 @@ variant unit_callable::get_value(const std::string& key) const
|
|||
res.push_back(variant(new attack_type_callable(att)));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
} else if(key == "abilities") {
|
||||
return formula_callable::convert_vector(u_.get_ability_list());
|
||||
} else if(key == "hitpoints") {
|
||||
|
@ -325,14 +326,14 @@ variant unit_type_callable::get_value(const std::string& key) const
|
|||
res.push_back(variant(config["id"].str()));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
} else if(key == "attacks") {
|
||||
std::vector<variant> res;
|
||||
for(const attack_type& att : u_.attacks()) {
|
||||
res.push_back(variant(new attack_type_callable(att)));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
} else if(key == "hitpoints" || key == "max_hitpoints") {
|
||||
return variant(u_.hitpoints());
|
||||
} else if(key == "experience" || key == "max_experience") {
|
||||
|
@ -411,7 +412,7 @@ variant config_callable::get_value(const std::string& key) const
|
|||
result.push_back(variant(new config_callable(child)));
|
||||
}
|
||||
|
||||
return variant(&result);
|
||||
return variant(result);
|
||||
} else if(key == "__all_children") {
|
||||
std::vector<variant> result;
|
||||
for(const auto& child : cfg_.all_children_range()) {
|
||||
|
@ -420,7 +421,7 @@ variant config_callable::get_value(const std::string& key) const
|
|||
result.push_back(kv);
|
||||
}
|
||||
|
||||
return variant(&result);
|
||||
return variant(result);
|
||||
} else if(key == "__children") {
|
||||
std::map<std::string, std::vector<variant> > build;
|
||||
for(const auto& child : cfg_.all_children_range()) {
|
||||
|
@ -430,17 +431,17 @@ variant config_callable::get_value(const std::string& key) const
|
|||
|
||||
std::map<variant,variant> result;
|
||||
for(auto& p : build) {
|
||||
result[variant(p.first)] = variant(&p.second);
|
||||
result[variant(p.first)] = variant(p.second);
|
||||
}
|
||||
|
||||
return variant(&result);
|
||||
return variant(result);
|
||||
} else if(key == "__attributes") {
|
||||
std::map<variant,variant> result;
|
||||
for(const auto& val : cfg_.attribute_range()) {
|
||||
result[variant(val.first)] = val.second.apply_visitor(fai_variant_visitor());
|
||||
}
|
||||
|
||||
return variant(&result);
|
||||
return variant(result);
|
||||
}
|
||||
|
||||
return variant();
|
||||
|
@ -556,7 +557,7 @@ variant gamemap_callable::get_value(const std::string& key) const
|
|||
}
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
} else if(key == "w") {
|
||||
return variant(gamemap_.w());
|
||||
} else if(key == "h") {
|
||||
|
@ -654,7 +655,7 @@ variant team_callable::get_value(const std::string& key) const
|
|||
result.push_back(variant(recruit));
|
||||
}
|
||||
|
||||
return variant(&result);
|
||||
return variant(result);
|
||||
} else if(key == "wml_vars") {
|
||||
return variant(new config_callable(team_.variables()));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "formula/callable.hpp"
|
||||
|
||||
#include "map/map.hpp"
|
||||
#include "units/unit.hpp"
|
||||
|
||||
class team;
|
||||
|
|
|
@ -92,7 +92,7 @@ private:
|
|||
for(size_t i = 0; i < function_names.size(); i++) {
|
||||
res.push_back(variant(function_names[i]));
|
||||
}
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
function_symbol_table* symbols_;
|
||||
|
@ -112,7 +112,7 @@ private:
|
|||
res.push_back((*i)->evaluate(variables,add_debug_info(fdb, 0, "[list element]")));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
std::vector<expression_ptr> items_;
|
||||
|
@ -170,7 +170,7 @@ private:
|
|||
res[ key ] = value;
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
std::vector<expression_ptr> items_;
|
||||
|
@ -238,7 +238,7 @@ public:
|
|||
for(char c : string_.as_string()) {
|
||||
chars.push_back(variant(std::string(1, c)));
|
||||
}
|
||||
return variant(&chars);
|
||||
return variant(chars);
|
||||
} else if(key == "word" || key == "words") {
|
||||
std::vector<variant> words;
|
||||
const std::string& str = string_.as_string();
|
||||
|
@ -249,7 +249,7 @@ public:
|
|||
words.push_back(variant(str.substr(last_space, next_space - last_space)));
|
||||
next_space = str.find_first_not_of(" \t", next_space);
|
||||
} while(next_space != std::string::npos);
|
||||
return variant(&words);
|
||||
return variant(words);
|
||||
} else if(key == "item" || key == "items") {
|
||||
std::vector<std::string> split = utils::parenthetical_split(string_.as_string(), ',');
|
||||
std::vector<variant> items;
|
||||
|
@ -257,7 +257,7 @@ public:
|
|||
for(const std::string s : split) {
|
||||
items.push_back(variant(s));
|
||||
}
|
||||
return variant(&items);
|
||||
return variant(items);
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ public:
|
|||
private:
|
||||
variant execute(const formula_callable& variables, formula_debugger *fdb) const {
|
||||
variant var = args()[0]->evaluate(variables, fdb);
|
||||
const formula_callable* callable = var.as_callable();
|
||||
const formula_callable* callable = var.as_callable().get();
|
||||
std::vector<formula_input> inputs = callable->inputs();
|
||||
std::vector<variant> res;
|
||||
for(size_t i=0; i<inputs.size(); ++i) {
|
||||
|
@ -104,7 +104,7 @@ private:
|
|||
res.push_back(variant(input.name));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -378,7 +378,7 @@ private:
|
|||
tmp.push_back( *it );
|
||||
}
|
||||
|
||||
return variant( &tmp );
|
||||
return variant(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -417,7 +417,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
return variant( &tmp );
|
||||
return variant(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -879,7 +879,7 @@ private:
|
|||
std::sort(vars.begin(), vars.end(), variant_comparator(args()[1], variables));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
return variant(vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -899,7 +899,7 @@ private:
|
|||
} else if(arg.is_list()) {
|
||||
std::vector<variant> list = args()[0]->evaluate(variables,fdb).as_list();
|
||||
std::reverse(list.begin(), list.end());
|
||||
return variant(&list);
|
||||
return variant(list);
|
||||
}
|
||||
return variant();
|
||||
}
|
||||
|
@ -973,8 +973,8 @@ private:
|
|||
}
|
||||
}
|
||||
if (items.is_map() )
|
||||
return variant(&map_vars);
|
||||
return variant(&list_vars);
|
||||
return variant(map_vars);
|
||||
return variant(list_vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1043,8 +1043,8 @@ private:
|
|||
}
|
||||
}
|
||||
if (items.is_map() )
|
||||
return variant(&map_vars);
|
||||
return variant(&list_vars);
|
||||
return variant(map_vars);
|
||||
return variant(list_vars);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1063,7 +1063,7 @@ private:
|
|||
break;
|
||||
}
|
||||
std::vector<variant> result(items.begin(), it);
|
||||
return variant(&result);
|
||||
return variant(result);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1112,9 +1112,9 @@ private:
|
|||
for(size_t i = 0; i < max_i; i++) {
|
||||
std::vector<variant> elem(input.size());
|
||||
std::transform(input.begin(), input.end(), elem.begin(), indexer(i));
|
||||
output.push_back(variant(&elem));
|
||||
output.push_back(variant(elem));
|
||||
}
|
||||
return variant(&output);
|
||||
return variant(output);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1160,7 +1160,7 @@ private:
|
|||
if (items[0].is_list() )
|
||||
{
|
||||
std::vector<variant> tmp;
|
||||
res = variant(&tmp);
|
||||
res = variant(tmp);
|
||||
if(args().size() >= 2) {
|
||||
res = args()[1]->evaluate(variables,fdb);
|
||||
if(!res.is_list())
|
||||
|
@ -1169,7 +1169,7 @@ private:
|
|||
} else if( items[0].is_map() )
|
||||
{
|
||||
std::map<variant,variant> tmp;
|
||||
res = variant(&tmp);
|
||||
res = variant(tmp);
|
||||
if(args().size() >= 2) {
|
||||
res = args()[1]->evaluate(variables,fdb);
|
||||
if(!res.is_map())
|
||||
|
@ -1212,7 +1212,7 @@ private:
|
|||
std::advance(end, count);
|
||||
std::vector<variant> res;
|
||||
std::copy(it, end, std::back_inserter(res));
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1236,7 +1236,7 @@ private:
|
|||
std::advance(it, -count);
|
||||
std::vector<variant> res;
|
||||
std::copy(it, items.end(), std::back_inserter(res));
|
||||
return variant(&res);
|
||||
return variant(res);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1479,9 +1479,9 @@ void key_value_pair::get_inputs(formula_input_vector* inputs) const {
|
|||
|
||||
void key_value_pair::serialize_to_string(std::string& str) const {
|
||||
str += "pair(";
|
||||
key_.serialize_to_string(str);
|
||||
str += key_.serialize_to_string();
|
||||
str += ",";
|
||||
value_.serialize_to_string(str);
|
||||
str += value_.serialize_to_string();
|
||||
str += ")";
|
||||
}
|
||||
|
||||
|
@ -1509,7 +1509,7 @@ variant formula_function_expression::execute(const formula_callable& variables,
|
|||
variant var = args()[n]->evaluate(variables,fdb);
|
||||
callable.add(arg_names_[n], var);
|
||||
if(static_cast<int>(n) == star_arg_) {
|
||||
callable.set_fallback(var.as_callable());
|
||||
callable.set_fallback(var.as_callable().get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,41 +15,39 @@
|
|||
#ifndef VARIANT_HPP_INCLUDED
|
||||
#define VARIANT_HPP_INCLUDED
|
||||
|
||||
#include "formula/variant_private.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "exceptions.hpp"
|
||||
|
||||
namespace game_logic {
|
||||
class formula_callable;
|
||||
// TODO: expand to cover variant as well
|
||||
namespace game_logic
|
||||
{
|
||||
class formula_callable;
|
||||
}
|
||||
|
||||
void push_call_stack(const char* str);
|
||||
void pop_call_stack();
|
||||
std::string get_call_stack();
|
||||
|
||||
struct call_stack_manager {
|
||||
explicit call_stack_manager(const char* str) {
|
||||
struct call_stack_manager
|
||||
{
|
||||
explicit call_stack_manager(const char* str)
|
||||
{
|
||||
push_call_stack(str);
|
||||
}
|
||||
|
||||
~call_stack_manager() {
|
||||
~call_stack_manager()
|
||||
{
|
||||
pop_call_stack();
|
||||
}
|
||||
};
|
||||
|
||||
class variant_iterator;
|
||||
|
||||
struct type_error : public game::error {
|
||||
explicit type_error(const std::string& str);
|
||||
};
|
||||
|
||||
|
||||
class variant {
|
||||
class variant
|
||||
{
|
||||
public:
|
||||
|
||||
enum TYPE { TYPE_NULL, TYPE_INT, TYPE_DECIMAL, TYPE_CALLABLE, TYPE_LIST, TYPE_STRING, TYPE_MAP };
|
||||
|
||||
enum DECIMAL_VARIANT_TYPE { DECIMAL_VARIANT };
|
||||
|
||||
variant();
|
||||
|
@ -57,60 +55,69 @@ public:
|
|||
variant(int n, DECIMAL_VARIANT_TYPE /*type*/);
|
||||
variant(double n, DECIMAL_VARIANT_TYPE /*type*/);
|
||||
explicit variant(const game_logic::formula_callable* callable);
|
||||
explicit variant(std::vector<variant>* array);
|
||||
explicit variant(const std::vector<variant>& array);
|
||||
explicit variant(const std::string& str);
|
||||
explicit variant(std::map<variant,variant>* map);
|
||||
~variant();
|
||||
explicit variant(const std::map<variant, variant>& map);
|
||||
|
||||
variant(const variant& v);
|
||||
variant& operator=(const variant& v);
|
||||
|
||||
variant operator[](size_t n) const;
|
||||
variant operator[](const variant& v) const;
|
||||
|
||||
size_t num_elements() const;
|
||||
bool is_empty() const;
|
||||
|
||||
variant get_member(const std::string& str) const;
|
||||
variant get_member(const std::string& name) const;
|
||||
|
||||
/** Functions to test the type of the internal value. */
|
||||
bool is_null() const { return type() == VARIANT_TYPE::TYPE_NULL; }
|
||||
bool is_int() const { return type() == VARIANT_TYPE::TYPE_INT; }
|
||||
bool is_decimal() const { return type() == VARIANT_TYPE::TYPE_DECIMAL; }
|
||||
bool is_callable() const { return type() == VARIANT_TYPE::TYPE_CALLABLE; }
|
||||
bool is_list() const { return type() == VARIANT_TYPE::TYPE_LIST; }
|
||||
bool is_string() const { return type() == VARIANT_TYPE::TYPE_STRING; }
|
||||
bool is_map() const { return type() == VARIANT_TYPE::TYPE_MAP; }
|
||||
|
||||
bool is_string() const { return type_ == TYPE_STRING; }
|
||||
bool is_null() const { return type_ == TYPE_NULL; }
|
||||
bool is_int() const { return type_ == TYPE_INT; }
|
||||
bool is_decimal() const { return type_ == TYPE_DECIMAL; }
|
||||
bool is_map() const { return type_ == TYPE_MAP; }
|
||||
int as_int() const;
|
||||
|
||||
//this function returns variant's internal representation of decimal number:
|
||||
//for example number 1.234 is represented as 1234
|
||||
/** Returns variant's internal representation of decimal number: ie, 1.234 is represented as 1234 */
|
||||
int as_decimal() const;
|
||||
|
||||
/** Returns a boolean state of the variant value. The implementation is type-dependent. */
|
||||
bool as_bool() const;
|
||||
|
||||
bool is_list() const { return type_ == TYPE_LIST; }
|
||||
|
||||
const std::vector<variant>& as_list() const;
|
||||
const std::map<variant,variant>& as_map() const;
|
||||
const std::map<variant, variant>& as_map() const;
|
||||
|
||||
const std::string& as_string() const;
|
||||
std::string type_string() const;
|
||||
|
||||
bool is_callable() const { return type_ == TYPE_CALLABLE; }
|
||||
const game_logic::formula_callable* as_callable() const {
|
||||
must_be(TYPE_CALLABLE); return callable_; }
|
||||
game_logic::formula_callable* mutable_callable() const {
|
||||
must_be(TYPE_CALLABLE); return mutable_callable_; }
|
||||
game_logic::const_formula_callable_ptr as_callable() const
|
||||
{
|
||||
must_be(VARIANT_TYPE::TYPE_CALLABLE);
|
||||
return value_cast<game_logic::variant_callable>()->get_callable();
|
||||
}
|
||||
|
||||
game_logic::formula_callable_ptr mutable_callable() const
|
||||
{
|
||||
must_be(VARIANT_TYPE::TYPE_CALLABLE);
|
||||
return value_cast<game_logic::variant_callable>()->get_callable_mutable();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* try_convert() const {
|
||||
T* try_convert() const
|
||||
{
|
||||
if(!is_callable()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<T*>(mutable_callable());
|
||||
return dynamic_cast<T*>(mutable_callable().get());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* convert_to() const {
|
||||
T* res = dynamic_cast<T*>(mutable_callable());
|
||||
T* convert_to() const
|
||||
{
|
||||
T* res = dynamic_cast<T*>(mutable_callable().get());
|
||||
if(!res) {
|
||||
throw type_error("could not convert type");
|
||||
}
|
||||
|
@ -128,8 +135,8 @@ public:
|
|||
|
||||
bool operator==(const variant&) const;
|
||||
bool operator!=(const variant&) const;
|
||||
bool operator<(const variant&) const;
|
||||
bool operator>(const variant&) const;
|
||||
bool operator<(const variant&) const;
|
||||
bool operator>(const variant&) const;
|
||||
bool operator<=(const variant&) const;
|
||||
bool operator>=(const variant&) const;
|
||||
|
||||
|
@ -139,6 +146,7 @@ public:
|
|||
variant list_elements_div(const variant& v) const;
|
||||
variant concatenate(const variant& v) const;
|
||||
variant build_range(const variant& v) const;
|
||||
|
||||
bool contains(const variant& other) const;
|
||||
|
||||
variant get_keys() const;
|
||||
|
@ -147,27 +155,43 @@ public:
|
|||
variant_iterator begin() const;
|
||||
variant_iterator end() const;
|
||||
|
||||
void serialize_to_string(std::string& str) const;
|
||||
//auto begin()->decltype(value_cast<game_logic::variant_callable>()->get_iter());
|
||||
//auto end()->decltype(value_cast<game_logic::variant_callable>()->get_iter());
|
||||
|
||||
std::string serialize_to_string() const;
|
||||
void serialize_from_string(const std::string& str);
|
||||
|
||||
std::string string_cast() const;
|
||||
|
||||
std::string to_debug_string(std::vector<const game_logic::formula_callable*>* seen=nullptr, bool verbose = false) const;
|
||||
std::string to_debug_string(game_logic::const_formula_callable_vec* seen = nullptr, bool verbose = false) const;
|
||||
|
||||
/** Gets string name of the current value type */
|
||||
std::string type_string() const
|
||||
{
|
||||
return type().to_string();
|
||||
}
|
||||
|
||||
private:
|
||||
void must_be(TYPE t) const;
|
||||
TYPE type_;
|
||||
union {
|
||||
int int_value_;
|
||||
int decimal_value_;
|
||||
const game_logic::formula_callable* callable_;
|
||||
game_logic::formula_callable* mutable_callable_;
|
||||
std::vector<variant>* list_;
|
||||
std::string* string_;
|
||||
std::map<variant,variant>* map_;
|
||||
};
|
||||
template<typename T>
|
||||
std::shared_ptr<T> value_cast() const
|
||||
{
|
||||
return game_logic::value_cast<T>(value_);
|
||||
}
|
||||
|
||||
void release();
|
||||
void must_be(VARIANT_TYPE t) const;
|
||||
|
||||
void must_both_be(VARIANT_TYPE t, const variant& second) const;
|
||||
|
||||
VARIANT_TYPE type() const
|
||||
{
|
||||
return value_->get_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant value.
|
||||
* Each of the constructors casts this to an appropriate helper class.
|
||||
*/
|
||||
game_logic::value_base_ptr value_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -178,14 +202,15 @@ private:
|
|||
* result in Undefined Behavior care should be taken when copying the
|
||||
* @p list_iterator_ and @p map_iterator_.
|
||||
*/
|
||||
class variant_iterator {
|
||||
class variant_iterator
|
||||
{
|
||||
public:
|
||||
typedef variant value_type;
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
typedef variant& reference;
|
||||
typedef variant* pointer;
|
||||
typedef int difference_type;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for a TYPE_NULL variant.
|
||||
*/
|
||||
|
@ -228,8 +253,9 @@ private:
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
T* convert_variant(const variant& v) {
|
||||
T* res = dynamic_cast<T*>(v.mutable_callable());
|
||||
T* convert_variant(const variant& v)
|
||||
{
|
||||
T* res = dynamic_cast<T*>(v.mutable_callable().get());
|
||||
if(!res) {
|
||||
throw type_error("could not convert type");
|
||||
}
|
||||
|
@ -237,16 +263,14 @@ T* convert_variant(const variant& v) {
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
T* try_convert_variant(const variant& v) {
|
||||
T* try_convert_variant(const variant& v)
|
||||
{
|
||||
if(!v.is_callable()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<T*>(v.mutable_callable());
|
||||
return dynamic_cast<T*>(v.mutable_callable().get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
281
src/formula/variant_private.cpp
Normal file
281
src/formula/variant_private.cpp
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "formula/variant.hpp"
|
||||
#include "formula/variant_private.hpp"
|
||||
|
||||
#include "formula/callable.hpp"
|
||||
|
||||
namespace game_logic
|
||||
{
|
||||
|
||||
variant variant_int::build_range_variant(int limit) const
|
||||
{
|
||||
const int len = std::abs(limit - value_) + 1;
|
||||
|
||||
std::vector<variant> res;
|
||||
res.reserve(len);
|
||||
|
||||
for(int i = value_; res.size() != res.capacity(); value_ < limit ? ++i : --i) {
|
||||
res.emplace_back(i);
|
||||
}
|
||||
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
std::string variant_decimal::to_string_impl(const bool sign_value) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
int fractional = value_ % 1000;
|
||||
int integer = (value_ - fractional) / 1000;
|
||||
|
||||
if(sign_value) {
|
||||
// Make sure we get the sign on small negative values.
|
||||
if(integer == 0 && value_ < 0) {
|
||||
ss << '-';
|
||||
}
|
||||
}
|
||||
|
||||
ss << integer << ".";
|
||||
|
||||
fractional = std::abs(fractional);
|
||||
|
||||
if(fractional < 100) {
|
||||
if(fractional < 10) {
|
||||
ss << "00";
|
||||
} else {
|
||||
ss << 0;
|
||||
}
|
||||
}
|
||||
|
||||
ss << fractional;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
variant_callable::variant_callable(const formula_callable* callable)
|
||||
: callable_(callable)
|
||||
, mutable_callable_(nullptr) // FIXME
|
||||
{}
|
||||
|
||||
std::string variant_callable::get_serialized_string() const
|
||||
{
|
||||
// TODO: make serialize return a string.
|
||||
std::string str;
|
||||
callable_->serialize(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string variant_callable::get_debug_string(const_formula_callable_vec& seen, bool verbose) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "{";
|
||||
|
||||
if(std::find(seen.begin(), seen.end(), callable_.get()) == seen.end()) {
|
||||
if(!verbose) {
|
||||
seen.push_back(callable_.get());
|
||||
}
|
||||
|
||||
formula_input_vector v = callable_->inputs();
|
||||
bool first = true;
|
||||
|
||||
for(const auto& input : v) {
|
||||
if(!first) {
|
||||
ss << ", ";
|
||||
}
|
||||
|
||||
first = false;
|
||||
ss << input.name << " ";
|
||||
|
||||
if(input.access == FORMULA_READ_WRITE) {
|
||||
ss << "(read-write) ";
|
||||
} else if(input.access == FORMULA_WRITE_ONLY) {
|
||||
ss << "(writeonly) ";
|
||||
}
|
||||
|
||||
ss << "-> " << callable_->query_value(input.name).to_debug_string(&seen, verbose);
|
||||
}
|
||||
} else {
|
||||
ss << "...";
|
||||
}
|
||||
|
||||
ss << "}";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool variant_callable::operator==(variant_value_base& other) const
|
||||
{
|
||||
return callable_->equals(value_ref_cast<variant_callable>(other).callable_.get());
|
||||
}
|
||||
|
||||
bool variant_callable::operator<=(variant_value_base& other) const
|
||||
{
|
||||
return value_ref_cast<variant_callable>(other).callable_->less(callable_.get());
|
||||
}
|
||||
|
||||
std::string variant_string::get_serialized_string() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "'";
|
||||
|
||||
for(const auto& c : string_) {
|
||||
switch(c) {
|
||||
case '\'':
|
||||
ss << "[']";
|
||||
break;
|
||||
case '[':
|
||||
ss << "[(]";
|
||||
break;
|
||||
case ']':
|
||||
ss << "[)]";
|
||||
break;
|
||||
default:
|
||||
ss << c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ss << "'";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string variant_container<T>::to_string_impl(bool annotate, bool annotate_empty, mod_func_t mod_func) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
if(annotate) {
|
||||
ss << "[";
|
||||
}
|
||||
|
||||
bool first_time = true;
|
||||
|
||||
for(const auto& member : container_) {
|
||||
if(!first_time) {
|
||||
ss << ",";
|
||||
}
|
||||
|
||||
first_time = false;
|
||||
|
||||
ss << to_string_detail(member, mod_func);
|
||||
}
|
||||
|
||||
// TODO: evaluate if this really needs to be separately conditional.
|
||||
if(annotate_empty && container_.empty()) {
|
||||
ss << "->";
|
||||
}
|
||||
|
||||
if(annotate) {
|
||||
ss << "]";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string variant_container<T>::string_cast() const
|
||||
{
|
||||
return to_string_impl(false, false, [](const variant& v) { return v.string_cast(); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string variant_container<T>::get_serialized_string() const
|
||||
{
|
||||
return to_string_impl(true, true, [](const variant& v) { return v.serialize_to_string(); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string variant_container<T>::get_debug_string(const_formula_callable_vec& seen, bool verbose) const
|
||||
{
|
||||
return to_string_impl(true, false, [&](const variant& v) { return v.to_debug_string(&seen, verbose); });
|
||||
}
|
||||
|
||||
// Force compilation of the following template instantiations
|
||||
template class variant_container<variant_vector>;
|
||||
template class variant_container<variant_map_raw>;
|
||||
|
||||
variant variant_list::list_op(value_base_ptr second, std::function<variant(variant&, variant&)> op_func)
|
||||
{
|
||||
const auto& other_list = value_cast<variant_list>(second);
|
||||
|
||||
if(num_elements() != other_list->num_elements()) {
|
||||
throw type_error("List op requires two lists of the same length");
|
||||
}
|
||||
|
||||
std::vector<variant> res;
|
||||
res.reserve(num_elements());
|
||||
|
||||
for(size_t i = 0; i < num_elements(); ++i) {
|
||||
res.push_back(op_func(get_container()[i], other_list->get_container()[i]));
|
||||
}
|
||||
|
||||
return variant(res);
|
||||
}
|
||||
|
||||
bool variant_list::operator==(variant_value_base& other) const
|
||||
{
|
||||
const auto& other_container = value_ref_cast<variant_list>(other).get_container();
|
||||
|
||||
if(num_elements() != other.num_elements()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t n = 0; n < num_elements(); ++n) {
|
||||
if(get_container()[n] != other_container[n]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool variant_list::operator<=(variant_value_base& other) const
|
||||
{
|
||||
const auto& other_container = value_ref_cast<variant_list>(other).get_container();
|
||||
|
||||
for(size_t n = 0; n != num_elements() && n != other.num_elements(); ++n) {
|
||||
if(get_container()[n] < other_container[n]) {
|
||||
return true;
|
||||
} else if(get_container()[n] > other_container[n]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return num_elements() <= other.num_elements();
|
||||
}
|
||||
|
||||
std::string variant_map::to_string_detail(const typename variant_map_raw::value_type& container_val, mod_func_t mod_func) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
ss << mod_func(container_val.first);
|
||||
ss << "->";
|
||||
ss << mod_func(container_val.second);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool variant_map::operator==(variant_value_base& other) const
|
||||
{
|
||||
return get_container() == value_ref_cast<variant_map>(other).get_container();
|
||||
}
|
||||
|
||||
bool variant_map::operator<=(variant_value_base& other) const
|
||||
{
|
||||
return get_container() <= value_ref_cast<variant_map>(other).get_container();
|
||||
}
|
||||
|
||||
} // namespace game_logic
|
466
src/formula/variant_private.hpp
Normal file
466
src/formula/variant_private.hpp
Normal file
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef VARIANT_PRIVATE_HPP_INCLUDED
|
||||
#define VARIANT_PRIVATE_HPP_INCLUDED
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "formula/callable_fwd.hpp"
|
||||
#include "utils/general.hpp"
|
||||
#include "utils/make_enum.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class variant;
|
||||
|
||||
/** The various types the variant class is designed to handle */
|
||||
MAKE_ENUM(VARIANT_TYPE,
|
||||
(TYPE_NULL, "null")
|
||||
(TYPE_INT, "int")
|
||||
(TYPE_DECIMAL, "decimal")
|
||||
(TYPE_CALLABLE, "object")
|
||||
(TYPE_LIST, "list")
|
||||
(TYPE_STRING, "string")
|
||||
(TYPE_MAP, "map")
|
||||
);
|
||||
|
||||
using variant_vector = std::vector<variant>;
|
||||
using variant_map_raw = std::map<variant, variant>;
|
||||
|
||||
struct type_error : public game::error
|
||||
{
|
||||
explicit type_error(const std::string& str);
|
||||
};
|
||||
|
||||
namespace game_logic
|
||||
{
|
||||
class formula_callable;
|
||||
class variant_value_base;
|
||||
|
||||
using value_base_ptr = std::shared_ptr<variant_value_base>;
|
||||
|
||||
template<typename T>
|
||||
static std::shared_ptr<T> value_cast(value_base_ptr ptr)
|
||||
{
|
||||
std::shared_ptr<T> res = std::dynamic_pointer_cast<T>(ptr);
|
||||
if(!res) {
|
||||
throw type_error("Could not cast type");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T& value_ref_cast(variant_value_base& ptr)
|
||||
{
|
||||
try {
|
||||
return dynamic_cast<T&>(ptr);
|
||||
} catch(std::bad_cast&) {
|
||||
throw type_error("Could not cast type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all variant type classes to inherit from.
|
||||
*
|
||||
* This provides a common interface for all type classes to implement, as well as
|
||||
* giving variant a base pointer type for its value member.
|
||||
*
|
||||
* Do note this class should implement *no* data members.
|
||||
*/
|
||||
class variant_value_base
|
||||
{
|
||||
public:
|
||||
/** Returns the number of elements in a type. Not relevant for every derivative. */
|
||||
virtual size_t num_elements() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool is_empty() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: evaluate how these three string-related functions could be combined or eliminated.
|
||||
virtual std::string string_cast() const
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
|
||||
virtual std::string get_serialized_string() const
|
||||
{
|
||||
return "null()";
|
||||
}
|
||||
|
||||
virtual std::string get_debug_string(const_formula_callable_vec& /*seen*/, bool /*verbose*/) const
|
||||
{
|
||||
return get_serialized_string();
|
||||
}
|
||||
|
||||
virtual bool as_bool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const
|
||||
{
|
||||
return other.get_type() == VARIANT_TYPE::TYPE_NULL;
|
||||
}
|
||||
|
||||
virtual bool operator<=(variant_value_base& /*other*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class variant_int : public virtual variant_value_base
|
||||
{
|
||||
public:
|
||||
explicit variant_int(int value) : value_(value) {}
|
||||
|
||||
virtual bool as_bool() const override
|
||||
{
|
||||
return value_ != 0;
|
||||
}
|
||||
|
||||
int get_integer() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
variant build_range_variant(int limit) const;
|
||||
|
||||
virtual std::string string_cast() const override
|
||||
{
|
||||
return std::to_string(value_);
|
||||
}
|
||||
|
||||
virtual std::string get_serialized_string() const override
|
||||
{
|
||||
return string_cast();
|
||||
}
|
||||
|
||||
virtual std::string get_debug_string(const_formula_callable_vec& /*seen*/, bool /*verbose*/) const override
|
||||
{
|
||||
return string_cast();
|
||||
}
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const override
|
||||
{
|
||||
return value_ == value_ref_cast<variant_int>(other).value_;
|
||||
}
|
||||
|
||||
virtual bool operator<=(variant_value_base& other) const override
|
||||
{
|
||||
return value_ <= value_ref_cast<variant_int>(other).value_;
|
||||
}
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const override
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_INT;
|
||||
}
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
|
||||
|
||||
class variant_decimal : public virtual variant_value_base
|
||||
{
|
||||
public:
|
||||
explicit variant_decimal(int value) : value_(value) {}
|
||||
|
||||
explicit variant_decimal(double value) : value_(0)
|
||||
{
|
||||
value *= 1000;
|
||||
value_ = static_cast<int>(value);
|
||||
value -= value_;
|
||||
|
||||
if(value > 0.5) {
|
||||
value_++;
|
||||
} else if(value < -0.5) {
|
||||
value_--;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool as_bool() const override
|
||||
{
|
||||
return value_ != 0;
|
||||
}
|
||||
|
||||
int get_decimal() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
virtual std::string string_cast() const override
|
||||
{
|
||||
return to_string_impl(false);
|
||||
}
|
||||
|
||||
virtual std::string get_serialized_string() const override
|
||||
{
|
||||
return to_string_impl(false);
|
||||
}
|
||||
|
||||
virtual std::string get_debug_string(const_formula_callable_vec& /*seen*/, bool /*verbose*/) const override
|
||||
{
|
||||
return to_string_impl(true);
|
||||
}
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const override
|
||||
{
|
||||
return value_ == value_ref_cast<variant_decimal>(other).value_;
|
||||
}
|
||||
|
||||
virtual bool operator<=(variant_value_base& other) const override
|
||||
{
|
||||
return value_ <= value_ref_cast<variant_decimal>(other).value_;
|
||||
}
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const override
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_DECIMAL;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string to_string_impl(const bool sign_value) const;
|
||||
|
||||
int value_;
|
||||
};
|
||||
|
||||
|
||||
class variant_callable : public virtual variant_value_base
|
||||
{
|
||||
public:
|
||||
explicit variant_callable(const formula_callable* callable);
|
||||
|
||||
virtual bool as_bool() const override
|
||||
{
|
||||
return callable_ != nullptr;
|
||||
}
|
||||
|
||||
virtual size_t num_elements() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const_formula_callable_ptr get_callable() const
|
||||
{
|
||||
return callable_;
|
||||
}
|
||||
|
||||
formula_callable_ptr get_callable_mutable()
|
||||
{
|
||||
return mutable_callable_;
|
||||
}
|
||||
|
||||
virtual std::string string_cast() const override
|
||||
{
|
||||
return "(object)";
|
||||
}
|
||||
|
||||
virtual std::string get_serialized_string() const override;
|
||||
|
||||
virtual std::string get_debug_string(const_formula_callable_vec& seen, bool verbose) const override;
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const override;
|
||||
virtual bool operator<=(variant_value_base& other) const override;
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const override
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_CALLABLE;
|
||||
}
|
||||
|
||||
private:
|
||||
const_formula_callable_ptr callable_;
|
||||
formula_callable_ptr mutable_callable_;
|
||||
};
|
||||
|
||||
|
||||
class variant_string : public virtual variant_value_base
|
||||
{
|
||||
public:
|
||||
explicit variant_string(const std::string& str) : string_(str) {}
|
||||
|
||||
virtual bool is_empty() const override
|
||||
{
|
||||
return string_.empty();
|
||||
}
|
||||
|
||||
virtual bool as_bool() const override
|
||||
{
|
||||
return !is_empty();
|
||||
}
|
||||
|
||||
const std::string& get_string() const
|
||||
{
|
||||
return string_;
|
||||
}
|
||||
|
||||
virtual std::string string_cast() const override
|
||||
{
|
||||
return string_;
|
||||
}
|
||||
|
||||
virtual std::string get_serialized_string() const override;
|
||||
|
||||
virtual std::string get_debug_string(const_formula_callable_vec& /*seen*/, bool /*verbose*/) const override
|
||||
{
|
||||
return string_;
|
||||
}
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const override
|
||||
{
|
||||
return string_ == value_ref_cast<variant_string>(other).string_;
|
||||
}
|
||||
|
||||
virtual bool operator<=(variant_value_base& other) const override
|
||||
{
|
||||
return string_ <= value_ref_cast<variant_string>(other).string_;
|
||||
}
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const override
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_STRING;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string string_;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class variant_container : public virtual variant_value_base
|
||||
{
|
||||
public:
|
||||
explicit variant_container(const T& container)
|
||||
: container_(container)
|
||||
, container_iter_(container_.begin())
|
||||
{
|
||||
// NOTE: add more conditions if this changes
|
||||
static_assert((std::is_same<variant_vector, T>::value || std::is_same<variant_map_raw, T>::value),
|
||||
"variant_container only accepts vector or map specifications.");
|
||||
}
|
||||
|
||||
virtual bool is_empty() const override
|
||||
{
|
||||
return container_.empty();
|
||||
}
|
||||
|
||||
virtual size_t num_elements() const override
|
||||
{
|
||||
return container_.size();
|
||||
}
|
||||
|
||||
virtual bool as_bool() const override
|
||||
{
|
||||
return !is_empty();
|
||||
}
|
||||
|
||||
T& get_container()
|
||||
{
|
||||
return container_;
|
||||
}
|
||||
|
||||
const T& get_container() const
|
||||
{
|
||||
return container_;
|
||||
}
|
||||
|
||||
virtual std::string string_cast() const override;
|
||||
|
||||
virtual std::string get_serialized_string() const override;
|
||||
|
||||
virtual std::string get_debug_string(const_formula_callable_vec& seen, bool verbose) const override;
|
||||
|
||||
bool contains(const variant& member) const
|
||||
{
|
||||
return util::contains<T, variant>(container_, member);
|
||||
}
|
||||
|
||||
protected:
|
||||
using mod_func_t = std::function<std::string(const variant&)>;
|
||||
|
||||
virtual std::string to_string_detail(const typename T::value_type& value, mod_func_t mod_func) const = 0;
|
||||
|
||||
private:
|
||||
std::string to_string_impl(bool annotate, bool annotate_empty, mod_func_t mod_func) const;
|
||||
|
||||
T container_;
|
||||
typename T::iterator container_iter_;
|
||||
};
|
||||
|
||||
|
||||
class variant_list : public variant_container<variant_vector>
|
||||
{
|
||||
public:
|
||||
explicit variant_list(const variant_vector& vec)
|
||||
: variant_container<variant_vector>(vec)
|
||||
{}
|
||||
|
||||
variant list_op(value_base_ptr second, std::function<variant(variant&, variant&)> op_func);
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const override;
|
||||
virtual bool operator<=(variant_value_base& other) const override;
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const override
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_LIST;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::string to_string_detail(const typename variant_vector::value_type& container_val, mod_func_t mod_func) const override
|
||||
{
|
||||
return mod_func(container_val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class variant_map : public variant_container<variant_map_raw>
|
||||
{
|
||||
public:
|
||||
explicit variant_map(const variant_map_raw& map)
|
||||
: variant_container<variant_map_raw>(map)
|
||||
{}
|
||||
|
||||
virtual bool operator==(variant_value_base& other) const override;
|
||||
virtual bool operator<=(variant_value_base& other) const override;
|
||||
|
||||
virtual const VARIANT_TYPE get_type() const override
|
||||
{
|
||||
return VARIANT_TYPE::TYPE_MAP;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::string to_string_detail(const typename variant_map_raw::value_type& container_val, mod_func_t mod_func) const override;
|
||||
};
|
||||
|
||||
using variant_container_vector = variant_container<variant_vector>;
|
||||
using variant_container_map = variant_container<variant_map_raw>;
|
||||
|
||||
} // namespace game_logic
|
||||
|
||||
#endif
|
|
@ -412,7 +412,7 @@ void styled_widget::update_canvas()
|
|||
// Possible TODO: Consider making a formula_callable for colours
|
||||
color_t link_color = get_link_color();
|
||||
std::vector<variant> link_color_as_list{variant(link_color.r), variant(link_color.g), variant(link_color.b), variant(link_color.a)};
|
||||
canvas.set_variable("text_link_color", variant(&link_color_as_list));
|
||||
canvas.set_variable("text_link_color", variant(link_color_as_list));
|
||||
canvas.set_variable("text_alignment",
|
||||
variant(encode_text_alignment(text_alignment_)));
|
||||
canvas.set_variable("text_maximum_width", variant(max_width));
|
||||
|
|
|
@ -52,13 +52,13 @@ public:
|
|||
lua_gettable(mState, table_i);
|
||||
values.push_back(luaW_tofaivariant(mState, -1));
|
||||
}
|
||||
return variant(&values);
|
||||
return variant(values);
|
||||
} else if(key == "__map") {
|
||||
std::map<variant,variant> values;
|
||||
for(lua_pushnil(mState); lua_next(mState, table_i); lua_pop(mState, 1)) {
|
||||
values[luaW_tofaivariant(mState, -2)] = luaW_tofaivariant(mState, -1);
|
||||
}
|
||||
return variant(&values);
|
||||
return variant(values);
|
||||
}
|
||||
lua_pushlstring(mState, key.c_str(), key.size());
|
||||
lua_gettable(mState, table_i);
|
||||
|
@ -152,7 +152,7 @@ void luaW_pushfaivariant(lua_State* L, variant val) {
|
|||
luaW_pushlocation(L, loc_ref->loc());
|
||||
} else {
|
||||
// If those fail, convert generically to a map
|
||||
const formula_callable* obj = val.as_callable();
|
||||
const formula_callable* obj = val.as_callable().get();
|
||||
std::vector<formula_input> inputs;
|
||||
obj->get_inputs(&inputs);
|
||||
lua_newtable(L);
|
||||
|
|
|
@ -47,9 +47,9 @@ class mock_party : public formula_callable {
|
|||
members.push_back(variant(&i_[n]));
|
||||
}
|
||||
|
||||
return variant(&members);
|
||||
return variant(members);
|
||||
} else if(key == "char") {
|
||||
return variant(&c_);
|
||||
return variant(c_);
|
||||
} else {
|
||||
return variant(0);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ void unit_formula_manager::write(config & cfg)
|
|||
std::string str;
|
||||
for(game_logic::map_formula_callable::const_iterator i = formula_vars_->begin(); i != formula_vars_->end(); ++i)
|
||||
{
|
||||
i->second.serialize_to_string(str);
|
||||
str = i->second.serialize_to_string();
|
||||
if (!str.empty())
|
||||
{
|
||||
ai_vars[i->first] = str;
|
||||
|
|
Loading…
Add table
Reference in a new issue