Merge branch 'lua_formula_bridge'

Allows calling WFL code from Lua
This commit is contained in:
Celtic Minstrel 2016-04-02 06:00:56 -04:00
commit cc7c124744
15 changed files with 580 additions and 203 deletions

View file

@ -717,6 +717,7 @@
91F462841C71139C0050A9C9 /* preferences_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91F462821C71139B0050A9C9 /* preferences_dialog.cpp */; };
91F462881C7115C50050A9C9 /* combobox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91F462861C7115C50050A9C9 /* combobox.cpp */; };
91F462941C7117400050A9C9 /* drop_down_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91F462921C7117400050A9C9 /* drop_down_list.cpp */; };
91FAC70A1C7FBC3400DAB2C3 /* lua_formula_bridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91FAC7091C7FBC2C00DAB2C3 /* lua_formula_bridge.cpp */; };
B504B94C1284C06B00261FE9 /* tips.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B504B94A1284C06B00261FE9 /* tips.cpp */; };
B508D13F10013BF900B12852 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B508D13E10013BF900B12852 /* Growl.framework */; };
B508D14B10013E4700B12852 /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = B508D14A10013E4700B12852 /* Growl Registration Ticket.growlRegDict */; };
@ -1713,6 +1714,8 @@
91F462921C7117400050A9C9 /* drop_down_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drop_down_list.cpp; sourceTree = "<group>"; };
91F462931C7117400050A9C9 /* drop_down_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = drop_down_list.hpp; sourceTree = "<group>"; };
91FAC70B1C80168600DAB2C3 /* group.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = group.hpp; sourceTree = "<group>"; };
91FAC7081C7F931900DAB2C3 /* lua_formula_bridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lua_formula_bridge.hpp; sourceTree = "<group>"; };
91FAC7091C7FBC2C00DAB2C3 /* lua_formula_bridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_formula_bridge.cpp; sourceTree = "<group>"; };
B504B94A1284C06B00261FE9 /* tips.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tips.cpp; sourceTree = "<group>"; };
B504B94B1284C06B00261FE9 /* tips.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tips.hpp; sourceTree = "<group>"; };
B508D13E10013BF900B12852 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = lib/Growl.framework; sourceTree = "<group>"; };
@ -4100,6 +4103,8 @@
91B621E61B76BB0B00B00E0F /* lua_cpp_function.hpp */,
ECA4A6781A1EC319006BCCF2 /* lua_fileops.cpp */,
91B621E71B76BB0E00B00E0F /* lua_fileops.hpp */,
91FAC7091C7FBC2C00DAB2C3 /* lua_formula_bridge.cpp */,
91FAC7081C7F931900DAB2C3 /* lua_formula_bridge.hpp */,
ECA1E0F11A1271AC00426E00 /* lua_gui2.cpp */,
91B621E81B76BB1100B00E0F /* lua_gui2.hpp */,
EC218E9F1A106648007C910C /* lua_kernel_base.cpp */,
@ -5182,6 +5187,7 @@
91DCA6891C9066CC0030F8D0 /* unit_preview_pane.cpp in Sources */,
91DCA68D1C9066EC0030F8D0 /* unit_recruit.cpp in Sources */,
9122417C1CAAB7B7008B347F /* loadscreen.cpp in Sources */,
91FAC70A1C7FBC3400DAB2C3 /* lua_formula_bridge.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -561,7 +561,8 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
void formula_ai::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
{
function_table_.add_formula_function(name,formula,precondition,args);
formula_function_ptr fcn(new user_formula_function(name,formula,precondition,args));
function_table_.add_function(name, fcn);
}
namespace {

View file

@ -376,7 +376,7 @@ private:
class nearest_loc_function : public function_expression {
public:
nearest_loc_function(const args_list& args, const formula_ai& /*ai*/)
nearest_loc_function(const args_list& args)
: function_expression("nearest_loc", args, 2, 2)
{
}
@ -409,7 +409,7 @@ private:
class adjacent_locs_function : public function_expression {
public:
adjacent_locs_function(const args_list& args, const formula_ai& /*ai*/)
adjacent_locs_function(const args_list& args)
: function_expression("adjacent_locs", args, 1, 1)
{
}
@ -433,7 +433,7 @@ private:
class locations_in_radius_function : public function_expression {
public:
locations_in_radius_function(const args_list& args, const formula_ai& /*ai*/)
locations_in_radius_function(const args_list& args)
: function_expression("locations_in_radius", args, 2, 2)
{
}
@ -505,7 +505,7 @@ private:
class castle_locs_function : public function_expression {
public:
castle_locs_function(const args_list& args, const formula_ai& /*ai*/)
castle_locs_function(const args_list& args)
: function_expression("castle_locs", args, 1, 1)
{
}
@ -565,7 +565,7 @@ private:
*/
class timeofday_modifier_function : public function_expression {
public:
timeofday_modifier_function(const args_list& args, const formula_ai& /*ai*/)
timeofday_modifier_function(const args_list& args)
: function_expression("timeofday_modifier", args, 1, 2)
{
}
@ -725,10 +725,9 @@ private:
const formula_ai& ai_;
};
class calculate_outcome_function : public function_expression {
public:
calculate_outcome_function(const args_list& args, const formula_ai& /*ai*/)
calculate_outcome_function(const args_list& args)
: function_expression( "calculate_outcome", args, 3, 4)
{
}
@ -812,7 +811,7 @@ private:
class outcomes_function : public function_expression {
public:
outcomes_function(const args_list& args, const formula_ai& /*ai*/)
outcomes_function(const args_list& args)
: function_expression("outcomes", args, 1, 1)
{
}
@ -1175,7 +1174,7 @@ private:
class attack_function : public function_expression {
public:
explicit attack_function(const args_list& args, const formula_ai& /*ai*/)
explicit attack_function(const args_list& args)
: function_expression("attack", args, 3, 4)
{}
private:
@ -1303,7 +1302,7 @@ private:
class unit_at_function : public function_expression {
public:
unit_at_function(const args_list& args, const formula_ai& /*ai*/)
unit_at_function(const args_list& args)
: function_expression("unit_at", args, 1, 1)
{}
private:
@ -1354,7 +1353,7 @@ private:
class units_can_reach_function : public function_expression {
public:
units_can_reach_function(const args_list& args, const formula_ai& /*ai*/)
units_can_reach_function(const args_list& args)
: function_expression("units_can_reach", args, 2, 2)
{}
private:
@ -1378,7 +1377,7 @@ private:
class defense_on_function : public function_expression {
public:
defense_on_function(const args_list& args, const formula_ai& /*ai*/)
defense_on_function(const args_list& args)
: function_expression("defense_on", args, 2, 2)
{}
private:
@ -1428,7 +1427,7 @@ private:
class chance_to_hit_function : public function_expression {
public:
chance_to_hit_function(const args_list& args, const formula_ai& /*ai*/)
chance_to_hit_function(const args_list& args)
: function_expression("chance_to_hit", args, 2, 2)
{}
private:
@ -1472,7 +1471,7 @@ private:
class movement_cost_function : public function_expression {
public:
movement_cost_function(const args_list& args, const formula_ai& /*ai*/)
movement_cost_function(const args_list& args)
: function_expression("movement_cost", args, 2, 2)
{}
private:
@ -1533,9 +1532,7 @@ private:
class max_possible_damage_function : public function_expression {
public:
max_possible_damage_function(
const args_list& args
, const formula_ai& /*ai*/)
max_possible_damage_function(const args_list& args)
: function_expression("max_possible_damage", args, 2, 2)
{}
private:
@ -1587,9 +1584,7 @@ private:
class max_possible_damage_with_retaliation_function : public function_expression {
public:
max_possible_damage_with_retaliation_function(
const args_list& args
, const formula_ai& /*ai*/)
max_possible_damage_with_retaliation_function(const args_list& args)
: function_expression("max_possible_damage_with_retaliation", args, 2, 2)
{}
private:
@ -1637,96 +1632,69 @@ private:
}
};
}
expression_ptr ai_function_symbol_table::create_function(const std::string &fn,
const std::vector<expression_ptr>& args) const {
if(fn == "outcomes") {
return expression_ptr(new outcomes_function(args, ai_));
//} else if(fn == "evaluate_for_position") {
// return expression_ptr(new evaluate_for_position_function(args, ai_));
} else if(fn == "move") {
return expression_ptr(new move_function(args));
} else if(fn == "move_partial") {
return expression_ptr(new move_partial_function(args));
} else if(fn == "attack") {
return expression_ptr(new attack_function(args, ai_));
} else if(fn == "rate_action") {
return expression_ptr(new rate_action_function(args, ai_));
} else if(fn == "recall") {
return expression_ptr(new recall_function(args));
} else if(fn == "recruit") {
return expression_ptr(new recruit_function(args));
} else if(fn == "safe_call") {
return expression_ptr(new safe_call_function(args));
} else if(fn == "get_unit_type") {
return expression_ptr(new get_unit_type_function(args));
} else if(fn == "is_avoided_location") {
return expression_ptr(new is_avoided_location_function(args,ai_));
} else if(fn == "is_village") {
return expression_ptr(new is_village_function(args));
} else if(fn == "is_unowned_village") {
return expression_ptr(new is_unowned_village_function(args, ai_));
} else if(fn == "unit_at") {
return expression_ptr(new unit_at_function(args, ai_));
} else if(fn == "unit_moves") {
return expression_ptr(new unit_moves_function(args, ai_));
} else if(fn == "set_var") {
return expression_ptr(new set_var_function(args));
} else if(fn == "set_unit_var") {
return expression_ptr(new set_unit_var_function(args));
} else if(fn == "fallback") {
return expression_ptr(new fallback_function(args));
} else if(fn == "units_can_reach") {
return expression_ptr(new units_can_reach_function(args, ai_));
} else if(fn == "debug_label") {
return expression_ptr(new debug_label_function(args, ai_));
} else if(fn == "defense_on") {
return expression_ptr(new defense_on_function(args, ai_));
} else if(fn == "chance_to_hit") {
return expression_ptr(new chance_to_hit_function(args, ai_));
} else if(fn == "movement_cost") {
return expression_ptr(new movement_cost_function(args, ai_));
} else if(fn == "max_possible_damage") {
return expression_ptr(new max_possible_damage_function(args, ai_));
} else if(fn == "max_possible_damage_with_retaliation") {
return expression_ptr(new max_possible_damage_with_retaliation_function(args, ai_));
} else if(fn == "next_hop") {
return expression_ptr(new next_hop_function(args, ai_));
} else if(fn == "adjacent_locs") {
return expression_ptr(new adjacent_locs_function(args, ai_));
} else if(fn == "locations_in_radius") {
return expression_ptr(new locations_in_radius_function(args, ai_));
} else if(fn == "castle_locs") {
return expression_ptr(new castle_locs_function(args, ai_));
} else if(fn == "timeofday_modifier") {
return expression_ptr(new timeofday_modifier_function(args, ai_));
} else if(fn == "distance_to_nearest_unowned_village") {
return expression_ptr(new distance_to_nearest_unowned_village_function(args, ai_));
} else if(fn == "shortest_path") {
return expression_ptr(new shortest_path_function(args, ai_));
} else if(fn == "simplest_path") {
return expression_ptr(new simplest_path_function(args, ai_));
} else if(fn == "nearest_keep") {
return expression_ptr(new nearest_keep_function(args, ai_));
} else if(fn == "suitable_keep") {
return expression_ptr(new suitable_keep_function(args, ai_));
} else if(fn == "nearest_loc") {
return expression_ptr(new nearest_loc_function(args, ai_));
} else if(fn == "find_shroud") {
return expression_ptr(new find_shroud_function(args, ai_));
} else if(fn == "close_enemies") {
return expression_ptr(new close_enemies_function(args, ai_));
} else if(fn == "calculate_outcome") {
return expression_ptr(new calculate_outcome_function(args, ai_));
} else if(fn == "run_file") {
return expression_ptr(new run_file_function(args, ai_));
} else if(fn == "calculate_map_ownership") {
return expression_ptr(new calculate_map_ownership_function(args, ai_));
} else {
return function_symbol_table::create_function(fn, args);
template<typename T>
class ai_formula_function : public formula_function {
protected:
formula_ai& ai_;
public:
ai_formula_function(const std::string& name, ai::formula_ai& ai) : formula_function(name), ai_(ai) {}
function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const {
return function_expression_ptr(new T(args, ai_));
}
}
};
}
// First macro is for functions taking an additional formula_ai argument.
// Functions using the second macro could potentially be made core.
#define AI_FUNCTION(name) add_function(#name, formula_function_ptr( \
new ai_formula_function<name##_function>(#name, ai)))
#define FUNCTION(name) add_function(#name, formula_function_ptr( \
new builtin_formula_function<name##_function>(#name)))
ai_function_symbol_table::ai_function_symbol_table(ai::formula_ai& ai) {
FUNCTION(outcomes);
//AI_FUNCTION(evaluate_for_position);
FUNCTION(move);
FUNCTION(move_partial);
FUNCTION(attack);
AI_FUNCTION(rate_action);
FUNCTION(recall);
FUNCTION(recruit);
FUNCTION(safe_call);
FUNCTION(get_unit_type);
AI_FUNCTION(is_avoided_location);
FUNCTION(is_village);
AI_FUNCTION(is_unowned_village);
FUNCTION(unit_at);
AI_FUNCTION(unit_moves);
FUNCTION(set_var);
FUNCTION(set_unit_var);
FUNCTION(fallback);
FUNCTION(units_can_reach);
AI_FUNCTION(debug_label);
FUNCTION(defense_on);
FUNCTION(chance_to_hit);
FUNCTION(movement_cost);
FUNCTION(max_possible_damage);
FUNCTION(max_possible_damage_with_retaliation);
AI_FUNCTION(next_hop);
FUNCTION(adjacent_locs);
FUNCTION(locations_in_radius);
FUNCTION(castle_locs);
FUNCTION(timeofday_modifier);
AI_FUNCTION(distance_to_nearest_unowned_village);
AI_FUNCTION(shortest_path);
AI_FUNCTION(simplest_path);
AI_FUNCTION(nearest_keep);
AI_FUNCTION(suitable_keep);
FUNCTION(nearest_loc);
AI_FUNCTION(find_shroud);
AI_FUNCTION(close_enemies);
FUNCTION(calculate_outcome);
AI_FUNCTION(run_file);
AI_FUNCTION(calculate_map_ownership);
}
#undef FUNCTION
}

View file

@ -28,17 +28,7 @@ namespace game_logic {
class ai_function_symbol_table : public function_symbol_table {
public:
explicit ai_function_symbol_table(ai::formula_ai& ai) :
ai_(ai),
move_functions()
{}
expression_ptr create_function(const std::string& fn,
const std::vector<expression_ptr>& args) const;
private:
ai::formula_ai& ai_;
std::set<std::string> move_functions;
explicit ai_function_symbol_table(ai::formula_ai& ai);
};
}

View file

@ -13,8 +13,10 @@
*/
#include "formula/callable_objects.hpp"
#include "formula/function.hpp"
#include "units/unit.hpp"
#include "units/formula_manager.hpp"
#include "config.hpp"
template <typename T, typename K>
variant convert_map( const std::map<T, K>& input_map ) {
@ -459,6 +461,80 @@ int unit_type_callable::do_compare(const formula_callable* callable) const
return u_.id().compare(u_callable->u_.id());
}
class fai_variant_visitor : public boost::static_visitor<variant> {
public:
variant operator()(bool b) const {return variant(b ? 1 : 0);}
variant operator()(int i) const {return variant(i);}
variant operator()(unsigned long long i) const {return variant(i);}
variant operator()(double i) const {return variant(i * 1000, variant::DECIMAL_VARIANT);}
variant operator()(const std::string& s) const {return variant(s);} // TODO: Should comma-separated lists of stuff be returned as a list? The challenge is to distinguish them from ordinary strings that happen to contain a comma (or should we assume that such strings will be translatable?)
variant operator()(const t_string& s) const {return variant(s.str());}
variant operator()(boost::blank) const {return variant();}
};
variant config_callable::get_value(const std::string& key) const
{
if(cfg_.has_attribute(key)) {
return cfg_[key].apply_visitor(fai_variant_visitor());
} else if(cfg_.has_child(key)) {
std::vector<variant> result;
for(const auto& child : cfg_.child_range(key)) {
result.push_back(variant(new config_callable(child)));
}
return variant(&result);
} else if(key == "__all_children") {
std::vector<variant> result;
for(const auto& child : cfg_.all_children_range()) {
const variant cfg_child(new config_callable(child.cfg));
const variant kv(new game_logic::key_value_pair(variant(child.key), cfg_child));
result.push_back(kv);
}
return variant(&result);
} else if(key == "__children") {
std::map<std::string, std::vector<variant> > build;
for(const auto& child : cfg_.all_children_range()) {
const variant cfg_child(new config_callable(child.cfg));
build[child.key].push_back(cfg_child);
}
std::map<variant,variant> result;
for(auto& p : build) {
result[variant(p.first)] = variant(&p.second);
}
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);
} else return variant();
}
void config_callable::get_inputs(std::vector<game_logic::formula_input>* inputs) const
{
inputs->push_back(game_logic::formula_input("__all_children", game_logic::FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("__children", game_logic::FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("__attributes", game_logic::FORMULA_READ_ONLY));
for(const auto& val : cfg_.attribute_range()) {
if(val.first.find_first_not_of(game_logic::formula::id_chars) != std::string::npos) {
inputs->push_back(game_logic::formula_input(val.first, game_logic::FORMULA_READ_ONLY));
}
}
}
int config_callable::do_compare(const game_logic::formula_callable* callable) const
{
const config_callable* cfg_callable = dynamic_cast<const config_callable*>(callable);
if(cfg_callable == NULL) {
return formula_callable::do_compare(callable);
}
if(cfg_ == cfg_callable->get_config()) {
return 0;
}
return cfg_.hash().compare(cfg_callable->get_config().hash());
}
variant terrain_callable::get_value(const std::string& key) const
{
if(key == "x") {

View file

@ -186,6 +186,17 @@ private:
const unit_type& u_;
};
class config_callable : public game_logic::formula_callable {
public:
config_callable(const config& c) : cfg_(c) {}
variant get_value(const std::string& key) const;
void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
int do_compare(const formula_callable* callable) const;
const config& get_config() const {return cfg_;}
private:
const config& cfg_;
};
CALLABLE_WRAPPER_START(team)
CALLABLE_WRAPPER_INPUT(side)

View file

@ -24,6 +24,8 @@
namespace game_logic
{
const char*const formula::id_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
void formula_callable::set_value(const std::string& key, const variant& /*value*/)
{
std::cerr << "ERROR: cannot set key '" << key << "' on object" << std::endl;
@ -1017,10 +1019,11 @@ expression_ptr parse_expression(const token* i1, const token* i2, function_symbo
if(symbols == nullptr) {
throw formula_error("Function symbol table required but not present", "",*i1->filename, i1->line_number);
}
symbols->add_formula_function(formula_name,
symbols->add_function(formula_name,
formula_function_ptr(new user_formula_function(formula_name,
const_formula_ptr(new formula(beg, i1, symbols)),
formula::create_optional_formula(precond, symbols),
args);
args)));
if((i1 == i2) || (i1 == (i2-1))) {
return expression_ptr(new function_list_expression(symbols));
}
@ -1345,6 +1348,7 @@ formula::formula(const std::string& str, function_symbol_table* symbols) :
expr_ = expression_ptr(new null_expression());
}
}
formula::formula(const token* i1, const token* i2, function_symbol_table* symbols) :
expr_(),
str_(),

View file

@ -62,6 +62,7 @@ public:
~formula();
const std::string& str() const { return str_; }
static const char*const id_chars;
private:
variant execute(const formula_callable& variables, formula_debugger *fdb = nullptr) const;
variant execute(formula_debugger *fdb) const;

View file

@ -1447,21 +1447,21 @@ variant formula_function_expression::execute(const formula_callable& variables,
return res;
}
function_expression_ptr formula_function::generate_function_expression(const std::vector<expression_ptr>& args) const
function_expression_ptr user_formula_function::generate_function_expression(const std::vector<expression_ptr>& args) const
{
return function_expression_ptr(new formula_function_expression(name_, args, formula_, precondition_, args_));
}
void function_symbol_table::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
void function_symbol_table::add_function(const std::string& name, formula_function_ptr fcn)
{
custom_formulas_[name] = formula_function(name, formula, precondition, args);
custom_formulas_[name] = fcn;
}
expression_ptr function_symbol_table::create_function(const std::string& fn, const std::vector<expression_ptr>& args) const
{
const std::map<std::string, formula_function>::const_iterator i = custom_formulas_.find(fn);
const functions_map::const_iterator i = custom_formulas_.find(fn);
if(i != custom_formulas_.end()) {
return i->second.generate_function_expression(args);
return i->second->generate_function_expression(args);
}
return expression_ptr();
@ -1470,7 +1470,7 @@ expression_ptr function_symbol_table::create_function(const std::string& fn, con
std::vector<std::string> function_symbol_table::get_function_names() const
{
std::vector<std::string> res;
for(std::map<std::string, formula_function>::const_iterator iter = custom_formulas_.begin(); iter != custom_formulas_.end(); ++iter ) {
for(functions_map::const_iterator iter = custom_formulas_.begin(); iter != custom_formulas_.end(); ++iter ) {
res.push_back((*iter).first);
}
return res;
@ -1478,45 +1478,12 @@ std::vector<std::string> function_symbol_table::get_function_names() const
namespace {
class base_function_creator {
public:
virtual expression_ptr create_function(const std::vector<expression_ptr>& args) const = 0;
virtual ~base_function_creator() {}
};
template<typename T>
class function_creator : public base_function_creator {
public:
virtual expression_ptr create_function(const std::vector<expression_ptr>& args) const {
return expression_ptr(new T(args));
}
virtual ~function_creator() {}
};
typedef std::map<std::string, base_function_creator*> functions_map;
// Takes ownership of the pointers, deleting them at program termination to
// suppress valgrind false positives
struct functions_map_manager {
functions_map map_;
~functions_map_manager() {
for (functions_map::value_type & v : map_) {
delete(v.second);
}
}
};
functions_map& get_functions_map() {
static functions_map_manager map_man;
functions_map & functions_table = map_man.map_;
#ifdef HAVE_VISUAL_LEAK_DETECTOR
VLDDisable();
#endif
function_symbol_table& get_functions_map() {
static function_symbol_table functions_table;
if(functions_table.empty()) {
#define FUNCTION(name) functions_table[#name] = new function_creator<name##_function>();
#define FUNCTION(name) functions_table.add_function(#name, \
formula_function_ptr(new builtin_formula_function<name##_function>(#name)))
FUNCTION(debug);
FUNCTION(dir);
FUNCTION(if);
@ -1525,7 +1492,7 @@ functions_map& get_functions_map() {
FUNCTION(min);
FUNCTION(max);
FUNCTION(choose);
FUNCTION(debug_float);
FUNCTION(debug_float);
FUNCTION(debug_print);
FUNCTION(debug_profile);
FUNCTION(wave);
@ -1596,26 +1563,18 @@ expression_ptr create_function(const std::string& fn,
return res;
}
}
//DBG_NG << "FN: '" << fn << "' " << fn.size() << "\n";
functions_map::const_iterator i = get_functions_map().find(fn);
if(i == get_functions_map().end()) {
throw formula_error("Unknow function: " + fn, "", "", 0);
}
return i->second->create_function(args);
}
std::vector<std::string> builtin_function_names()
{
std::vector<std::string> res;
const functions_map& m = get_functions_map();
for(functions_map::const_iterator i = m.begin(); i != m.end(); ++i) {
res.push_back(i->first);
expression_ptr res(get_functions_map().create_function(fn, args));
if(!res) {
throw formula_error("Unknown function: " + fn, "", "", 0);
}
return res;
}
std::vector<std::string> builtin_function_names()
{
return get_functions_map().get_function_names();
}
}

View file

@ -96,38 +96,48 @@ private:
typedef boost::shared_ptr<function_expression> function_expression_ptr;
class formula_function {
protected:
std::string name_;
public:
formula_function(const std::string name) : name_(name) {}
virtual function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const = 0;
virtual ~formula_function() {}
};
class user_formula_function : public formula_function {
const_formula_ptr formula_;
const_formula_ptr precondition_;
std::vector<std::string> args_;
public:
formula_function() :
name_(),
formula_(),
precondition_(),
args_()
{
}
formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args) : name_(name), formula_(formula), precondition_(precondition), args_(args)
user_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
: formula_function(name)
, formula_(formula)
, precondition_(precondition)
, args_(args)
{}
function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const;
};
class function_symbol_table {
std::map<std::string, formula_function> custom_formulas_;
template<typename T>
class builtin_formula_function : public formula_function {
public:
function_symbol_table() :
custom_formulas_()
{
builtin_formula_function(const std::string& name) : formula_function(name) {}
function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const {
return function_expression_ptr(new T(args));
}
};
virtual ~function_symbol_table() {}
virtual void add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args);
virtual expression_ptr create_function(const std::string& fn,
const std::vector<expression_ptr>& args) const;
typedef boost::shared_ptr<formula_function> formula_function_ptr;
typedef std::map<std::string, formula_function_ptr> functions_map;
class function_symbol_table {
functions_map custom_formulas_;
public:
void add_function(const std::string& name, formula_function_ptr fcn);
expression_ptr create_function(const std::string& fn, const std::vector<expression_ptr>& args) const;
std::vector<std::string> get_function_names() const;
bool empty() {return custom_formulas_.empty();}
};
expression_ptr create_function(const std::string& fn,

View file

@ -75,6 +75,7 @@
#include "scripting/lua_api.hpp" // for luaW_toboolean, etc
#include "scripting/lua_common.hpp"
#include "scripting/lua_cpp_function.hpp"
#include "scripting/lua_formula_bridge.hpp"
#include "scripting/lua_gui2.hpp" // for show_gamestate_inspector
#include "scripting/lua_pathfind_cost_calculator.hpp"
#include "scripting/lua_race.hpp"
@ -4302,11 +4303,13 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle
{ "add_known_unit", &intf_add_known_unit },
{ "add_modification", &intf_add_modification },
{ "advance_unit", &intf_advance_unit },
{ "compile_formula", &lua_formula_bridge::intf_compile_formula},
{ "copy_unit", &intf_copy_unit },
{ "create_unit", &intf_create_unit },
{ "debug", &intf_debug },
{ "debug_ai", &intf_debug_ai },
{ "eval_conditional", &intf_eval_conditional },
{ "eval_formula", &lua_formula_bridge::intf_eval_formula},
{ "get_era", &intf_get_era },
{ "get_image_size", &intf_get_image_size },
{ "get_time_stamp", &intf_get_time_stamp },
@ -4500,6 +4503,9 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle
lua_pushstring(L, "unit variables");
lua_setfield(L, -2, "__metatable");
lua_rawset(L, LUA_REGISTRYINDEX);
// Create formula bridge metatables
cmd_log_ << lua_formula_bridge::register_metatables(L);
// Create the vconfig metatable.
cmd_log_ << lua_common::register_vconfig_metatable(L);

View file

@ -0,0 +1,296 @@
/*
Part of 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 "scripting/lua_formula_bridge.hpp"
#include "boost/variant/static_visitor.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "scripting/lua_api.hpp"
#include "scripting/lua_common.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
#include "formula/callable_objects.hpp"
#include "formula/formula.hpp"
#include "variable.hpp"
#include "resources.hpp"
#include "units/map.hpp"
void luaW_pushfaivariant(lua_State* L, variant val);
variant luaW_tofaivariant(lua_State* L, int i);
using namespace game_logic;
class lua_callable : public formula_callable {
lua_State* mState;
int table_i;
public:
lua_callable(lua_State* L, int i) : mState(L), table_i(lua_absindex(L,i)) {}
variant get_value(const std::string& key) const {
if(key == "__list") {
std::vector<variant> values;
size_t n = lua_rawlen(mState, table_i);
if(n == 0) {
return variant();
}
for(size_t i = 1; i <= n; i++) {
lua_pushinteger(mState, i);
lua_gettable(mState, table_i);
values.push_back(luaW_tofaivariant(mState, -1));
}
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);
}
lua_pushlstring(mState, key.c_str(), key.size());
lua_gettable(mState, table_i);
variant result = luaW_tofaivariant(mState, -1);
lua_pop(mState, 1);
return result;
}
void get_inputs(std::vector<formula_input>* inputs) const {
inputs->push_back(formula_input("__list", FORMULA_READ_ONLY));
inputs->push_back(formula_input("__map", FORMULA_READ_ONLY));
for(lua_pushnil(mState); lua_next(mState, table_i); lua_pop(mState,1)) {
if(lua_isstring(mState, -2) && !lua_isnumber(mState, -2)) {
std::string key = lua_tostring(mState, -2);
if(key.find_first_not_of(formula::id_chars) != std::string::npos) {
inputs->push_back(formula_input(key, FORMULA_READ_ONLY));
}
}
}
}
int do_compare(const formula_callable* other) const {
const lua_callable* lua = dynamic_cast<const lua_callable*>(other);
if(lua == NULL) {
return formula_callable::do_compare(other);
}
if(mState == lua->mState) { // Which should always be the case, but let's be safe here
if(lua_compare(mState, table_i, lua->table_i, LUA_OPEQ)) {
return 0;
}
int top = lua_gettop(mState);
if(lua_getmetatable(mState, table_i)) {
lua_getfield(mState, -1, "__lt");
if(!lua_isnoneornil(mState, -1)) {
if(lua_getmetatable(mState, lua->table_i)) {
lua_getfield(mState, -1, "__lt");
if(!lua_isnoneornil(mState, -1)) {
lua_settop(mState, top);
return lua_compare(mState, table_i, lua->table_i, LUA_OPLT) ? -1 : 1;
}
if(lua_compare(mState, -4, -2, LUA_OPEQ)) {
lua_settop(mState, top);
return 0;
}
const void* lhs = lua_topointer(mState, -4);
const void* rhs = lua_topointer(mState, -2);
lua_settop(mState, top);
return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0);
}
}
}
lua_settop(mState, top);
return lua_topointer(mState, -2) < lua_topointer(mState, -1) ? -1 : 1;
}
return mState < lua->mState ? -1 : 1;
}
};
void luaW_pushfaivariant(lua_State* L, variant val) {
if(val.is_int()) {
lua_pushinteger(L, val.as_int());
} else if(val.is_decimal()) {
lua_pushnumber(L, val.as_decimal() / 1000.0);
} else if(val.is_string()) {
const std::string result_string = val.as_string();
lua_pushlstring(L, result_string.c_str(), result_string.size());
} else if(val.is_list()) {
lua_newtable(L);
for(const variant& v : val.as_list()) {
lua_pushinteger(L, lua_rawlen(L, -1) + 1);
luaW_pushfaivariant(L, v);
lua_settable(L, -3);
}
} else if(val.is_map()) {
typedef std::map<variant,variant>::value_type kv_type;
lua_newtable(L);
for(const kv_type& v : val.as_map()) {
luaW_pushfaivariant(L, v.first);
luaW_pushfaivariant(L, v.second);
lua_settable(L, -3);
}
} else if(val.is_callable()) {
// First try a few special cases
if(unit_callable* u_ref = val.try_convert<unit_callable>()) {
const unit& u = u_ref->get_unit();
unit_map::iterator un_it = resources::units->find(u.get_location());
if(&*un_it == &u) {
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(u.underlying_id());
} else {
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(u.side(), u.underlying_id());
}
lua_pushlightuserdata(L, getunitKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, -2);
} else if(location_callable* loc_ref = val.try_convert<location_callable>()) {
luaW_pushlocation(L, loc_ref->loc());
} else {
// If those fail, convert generically to a map
const formula_callable* obj = val.as_callable();
std::vector<formula_input> inputs;
obj->get_inputs(&inputs);
lua_newtable(L);
for(const formula_input& attr : inputs) {
if(attr.access == FORMULA_WRITE_ONLY) {
continue;
}
lua_pushstring(L, attr.name.c_str());
luaW_pushfaivariant(L, obj->query_value(attr.name));
lua_settable(L, -3);
}
}
} else if(val.is_null()) {
lua_pushnil(L);
}
}
variant luaW_tofaivariant(lua_State* L, int i) {
switch(lua_type(L, i)) {
case LUA_TBOOLEAN:
return variant(lua_tointeger(L, i));
case LUA_TNUMBER:
return variant(lua_tonumber(L, i), variant::DECIMAL_VARIANT);
case LUA_TSTRING:
return variant(lua_tostring(L, i));
case LUA_TTABLE:
return variant(new lua_callable(L, i));
case LUA_TUSERDATA:
static t_string tstr;
static vconfig vcfg = vconfig::unconstructed_vconfig();
static map_location loc;
if(luaW_totstring(L, i, tstr)) {
return variant(tstr.str());
} else if(luaW_tovconfig(L, i, vcfg)) {
return variant(new config_callable(vcfg.get_parsed_config()));
} else if(unit* u = luaW_tounit(L, i)) {
return variant(new unit_callable(*u));
} else if(luaW_tolocation(L, i, loc)) {
return variant(new location_callable(loc));
}
break;
}
return variant();
}
/**
* Evaluates a formula in the formula engine.
* - Arg 1: Formula string.
* - Arg 2: optional context; can be a unit or a Lua table.
* - Ret 1: Result of the formula.
*/
int lua_formula_bridge::intf_eval_formula(lua_State *L)
{
bool need_delete = false;
fwrapper* form;
if(luaW_hasmetatable(L, 1, formulaKey)) {
form = static_cast<fwrapper*>(lua_touserdata(L, 1));
} else {
need_delete = true;
form = new fwrapper(luaL_checkstring(L, 1));
}
boost::shared_ptr<formula_callable> context, fallback;
if(unit* u = luaW_tounit(L, 2)) {
context.reset(new unit_callable(*u));
} else if(lua_istable(L, 2)) {
context.reset(new lua_callable(L, 2));
} else {
context.reset(new map_formula_callable);
}
variant result = form->evaluate(*context);
luaW_pushfaivariant(L, result);
if(need_delete) {
delete form;
}
return 1;
}
int lua_formula_bridge::intf_compile_formula(lua_State* L)
{
if(!lua_isstring(L, 1)) {
luaL_typerror(L, 1, "string");
}
new(lua_newuserdata(L, sizeof(fwrapper))) fwrapper(lua_tostring(L, 1));
lua_pushlightuserdata(L, formulaKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, -2);
return 1;
}
lua_formula_bridge::fwrapper::fwrapper(const std::string& code, game_logic::function_symbol_table* functions)
: formula_ptr(new formula(code, functions))
{
}
std::string lua_formula_bridge::fwrapper::str() const
{
if(formula_ptr) {
return formula_ptr->str();
}
return "";
}
variant lua_formula_bridge::fwrapper::evaluate(const formula_callable& variables, formula_debugger* fdb) const
{
if(formula_ptr) {
return formula_ptr->evaluate(variables, fdb);
}
return variant();
}
static int impl_formula_collect(lua_State* L)
{
lua_formula_bridge::fwrapper* form = static_cast<lua_formula_bridge::fwrapper*>(lua_touserdata(L, 1));
form->~fwrapper();
return 0;
}
static int impl_formula_tostring(lua_State* L)
{
lua_formula_bridge::fwrapper* form = static_cast<lua_formula_bridge::fwrapper*>(lua_touserdata(L, 1));
const std::string str = form->str();
lua_pushlstring(L, str.c_str(), str.size());
return 1;
}
std::string lua_formula_bridge::register_metatables(lua_State* L)
{
lua_pushlightuserdata(L, formulaKey);
lua_createtable(L, 0, 4);
lua_pushcfunction(L, impl_formula_collect);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, impl_formula_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, intf_eval_formula);
lua_setfield(L, -2, "__call");
lua_pushstring(L, "formula");
lua_setfield(L, -2, "__metatable");
lua_rawset(L, LUA_REGISTRYINDEX);
return "Adding formula metatable...";
}

View file

@ -0,0 +1,46 @@
/*
Part of 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 LUA_FORMULA_BRIDGE_HPP_INCLUDED
#define LUA_FORMULA_BRIDGE_HPP_INCLUDED
#include <string>
#include <boost/shared_ptr.hpp>
struct lua_State;
class variant;
namespace game_logic {
class formula;
class function_symbol_table;
class formula_debugger;
class formula_callable;
}
namespace lua_formula_bridge {
int intf_eval_formula(lua_State*);
int intf_compile_formula(lua_State*);
std::string register_metatables(lua_State*);
class fwrapper {
boost::shared_ptr<game_logic::formula> formula_ptr;
public:
fwrapper(const std::string& code, game_logic::function_symbol_table* functions = NULL);
std::string str() const;
variant evaluate(const game_logic::formula_callable& variables, game_logic::formula_debugger* fdb = NULL) const;
};
} // end namespace lua_formula_bridge
#endif

View file

@ -21,6 +21,7 @@ static char const v_unitvarKey = 0;
static char const v_ustatusKey = 0;
static char const v_uattacksKey = 0;
static char const v_uattackKey = 0;
static char const v_formulaKey = 0;
luatypekey const executeKey = static_cast<void *>(const_cast<char *>(&v_executeKey));
@ -29,3 +30,4 @@ luatypekey const unitvarKey = static_cast<void *>(const_cast<char *>(&v_unitvarK
luatypekey const ustatusKey = static_cast<void *>(const_cast<char *>(&v_ustatusKey));
luatypekey const uattacksKey = static_cast<void *>(const_cast<char *>(&v_uattacksKey));
luatypekey const uattackKey = static_cast<void *>(const_cast<char *>(&v_uattackKey));
luatypekey const formulaKey = static_cast<void *>(const_cast<char *>(&v_formulaKey));

View file

@ -25,5 +25,6 @@ extern luatypekey const unitvarKey;
extern luatypekey const ustatusKey;
extern luatypekey const uattacksKey;
extern luatypekey const uattackKey;
extern luatypekey const formulaKey;
#endif