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:
Charles Dang 2017-03-31 21:58:32 +11:00
parent d8c670654a
commit 99874f4253
22 changed files with 1202 additions and 915 deletions

View file

@ -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" />

View file

@ -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

View file

@ -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)

View file

@ -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") {

View file

@ -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;

View file

@ -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();
}

View file

@ -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;

View file

@ -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);
}
};

View file

@ -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_ )

View file

@ -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

View file

@ -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()));
}

View file

@ -17,7 +17,6 @@
#include "formula/callable.hpp"
#include "map/map.hpp"
#include "units/unit.hpp"
class team;

View file

@ -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();
}

View file

@ -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

View file

@ -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

View 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

View 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

View file

@ -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));

View file

@ -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);

View file

@ -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);
}

View file

@ -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;