Add wml.interpolate function for vconfig interpolation from a source other than the game variables

Unlike wml.tovconfig, this method works even in the plugin and map generation contexts.
This commit is contained in:
Celtic Minstrel 2019-12-02 22:43:13 -05:00
parent 86197e857b
commit 027be31826
7 changed files with 136 additions and 70 deletions

View file

@ -637,7 +637,7 @@ else
return setmetatable(cfg, fake_vconfig_mt)
end
wesnoth.tovconfig = wesnoth.deprecate_api('wesnoth.tovconfig', 'wml.valid', 1, null, tovconfig_fake, 'tovconfig is now deprecated in plugin or map generation contexts; if you need to check whether a table is valid as a WML object, use wml.valid instead.')
wesnoth.tovconfig = wesnoth.deprecate_api('wesnoth.tovconfig', 'wml.valid or wml.interpolate', 1, null, tovconfig_fake, 'tovconfig is now deprecated in plugin or map generation contexts; if you need to check whether a table is valid as a WML object, use wml.valid instead, and use wml.interpolate if you need to substitute variables into a WML object.')
wml.tovconfig = wesnoth.deprecate_api('wml.tovconfig', 'wml.valid', 1, null, tovconfig_fake, 'tovconfig is now deprecated in plugin or map generation contexts; if you need to check whether a table is valid as a WML object, use wml.valid instead.')
wml.literal = wesnoth.deprecate_api('wml.literal', '(no replacement)', 1, null, wml.literal, 'Since vconfigs are not supported outside of the game kernel, this function is redundant and will be removed from plugin and map generation contexts. It will continue to work in the game kernel.')
wml.parsed = wesnoth.deprecate_api('wml.parsed', '(no replacement)', 1, null, wml.parsed, 'Since vconfigs are not supported outside of the game kernel, this function is redundant and will be removed from plugin and map generation contexts. It will continue to work in the game kernel.')

View file

@ -815,10 +815,3 @@ inline config::config(config_key_type first, T&&... args)
detail::config_construct_unpacker<config_key_type, T...> unpack;
unpack.visit(*this, first, std::forward<T>(args)...);
}
class variable_set
{
public:
virtual ~variable_set() {}
virtual config::attribute_value get_variable_const(const std::string &id) const = 0;
};

View file

@ -16,6 +16,7 @@
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "formula/string_utils.hpp"
#include "variable.hpp"
#include "config.hpp"
#include "log.hpp"
@ -47,9 +48,17 @@ public:
val = itor->second;
return val;
}
virtual variable_access_const get_variable_access_read(const std::string& varname) const
{
temp_.reset(new config);
for(const auto& p : map_) {
temp_->insert(p.first, p.second);
}
return variable_access_const(varname, *temp_);
}
private:
const std::map<std::string,T>& map_;
mutable std::shared_ptr<config> temp_; // only used if get_variable_access_read called
};
}

View file

@ -818,6 +818,20 @@ static int intf_clone_wml(lua_State* L)
return 1;
}
/**
* Interpolates variables into a WML table, including [insert_tag]
* Arg 1: WML table to interpolate into
* Arg 2: WML table of variables
*/
static int intf_wml_interpolate(lua_State* L)
{
config cfg = luaW_checkconfig(L, 1), vars_cfg = luaW_checkconfig(L, 2);
config_variable_set vars(vars_cfg);
vconfig vcfg(cfg, vars);
luaW_pushconfig(L, vcfg.get_parsed_config());
return 1;
}
// End Callback implementations
// Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
@ -981,6 +995,7 @@ lua_kernel_base::lua_kernel_base()
{ "valid", &intf_wml_valid},
{ "matches_filter", &intf_wml_matches_filter},
{ "tostring", &intf_wml_tostring},
{ "interpolate", &intf_wml_interpolate},
{ nullptr, nullptr },
};
lua_newtable(L);

View file

@ -39,25 +39,33 @@ static lg::log_domain log_engine("engine");
namespace
{
const config as_nonempty_range_default("_");
config::const_child_itors as_nonempty_range(const std::string& varname)
config::const_child_itors as_nonempty_range(const std::string& varname, const variable_set& vars)
{
config::const_child_itors range = as_nonempty_range_default.child_range("_");
config::const_child_itors range = vars.get_variable_access_read(varname).as_array();
if(resources::gamedata) {
config::const_child_itors temp_range = resources::gamedata->get_variable_access_read(varname).as_array();
if(!temp_range.empty()) {
range = temp_range;
}
if(!range.empty()) {
return range;
}
return range;
return as_nonempty_range_default.child_range("_");
}
struct : public variable_set
{
config::attribute_value get_variable_const(const std::string&) const override
{
return config::attribute_value();
}
variable_access_const get_variable_access_read(const std::string& varname) const override
{
return variable_access_const(varname, config());
}
} null_variable_set;
}
config::attribute_value config_variable_set::get_variable_const(const std::string &id) const {
try {
variable_access_const variable(id, cfg_);
variable_access_const variable = get_variable_access_read(id);
return variable.as_scalar();
} catch(const invalid_variablename_exception&) {
ERR_NG << "invalid variablename " << id << "\n";
@ -65,16 +73,27 @@ config::attribute_value config_variable_set::get_variable_const(const std::strin
}
}
variable_access_const config_variable_set::get_variable_access_read(const std::string &id) const {
return variable_access_const(id, cfg_);
}
const config vconfig::default_empty_config = config();
static const variable_set* try_get_gamedata()
{
if(resources::gamedata) {
return resources::gamedata;
}
return &null_variable_set;
}
vconfig::vconfig() :
cache_(), cfg_(&default_empty_config)
cache_(), cfg_(&default_empty_config), variables_(try_get_gamedata())
{
}
vconfig::vconfig(const config & cfg, const std::shared_ptr<const config> & cache) :
cache_(cache), cfg_(&cfg)
cache_(cache), cfg_(&cfg), variables_(try_get_gamedata())
{
}
@ -88,12 +107,29 @@ vconfig::vconfig(const config & cfg, const std::shared_ptr<const config> & cache
* If in doubt, set to true; it is less efficient, but safe.
* See also make_safe().
*/
vconfig::vconfig(const config &cfg, bool manage_memory) :
cache_(manage_memory ? new config(cfg) : nullptr),
cfg_(manage_memory ? cache_.get() : &cfg)
vconfig::vconfig(const config &cfg, bool manage_memory, const variable_set* vars)
: cache_(manage_memory ? new config(cfg) : nullptr)
, cfg_(manage_memory ? cache_.get() : &cfg)
, variables_(vars ? vars : try_get_gamedata())
{
}
vconfig::vconfig(const config &cfg)
: cache_(), cfg_(&cfg), variables_(try_get_gamedata())
{}
vconfig::vconfig(config &&cfg)
: cache_(new config(std::move(cfg))), cfg_(cache_.get()), variables_(try_get_gamedata())
{}
vconfig::vconfig(const config& cfg, const std::shared_ptr<const config> & cache, const variable_set& variables)
: cache_(cache), cfg_(&cfg), variables_(&variables)
{}
vconfig::vconfig(const config& cfg, const variable_set& variables)
: cache_(), cfg_(&cfg), variables_(&variables)
{}
/**
* Default destructor, but defined here for possibly faster compiles
* (templates sometimes can be rough on the compiler).
@ -151,7 +187,7 @@ config vconfig::get_parsed_config() const
for (const config::any_child &child : cfg_->all_children_range())
{
if (child.key == "insert_tag") {
vconfig insert_cfg(child.cfg);
vconfig insert_cfg(child.cfg, *variables_);
std::string name = insert_cfg["name"];
std::string vname = insert_cfg["variable"];
if(!vconfig_recursion.insert(vname).second) {
@ -159,10 +195,10 @@ config vconfig::get_parsed_config() const
}
try
{
config::const_child_itors range = as_nonempty_range(vname);
config::const_child_itors range = as_nonempty_range(vname, *variables_);
for (const config& ch : range)
{
res.add_child(name, vconfig(ch).get_parsed_config());
res.add_child(name, vconfig(ch, *variables_).get_parsed_config());
}
}
catch(const invalid_variablename_exception&)
@ -181,7 +217,7 @@ config vconfig::get_parsed_config() const
}
vconfig_recursion.erase(vname);
} else {
res.add_child(child.key, vconfig(child.cfg).get_parsed_config());
res.add_child(child.key, vconfig(child.cfg, *variables_).get_parsed_config());
}
}
return res;
@ -194,17 +230,17 @@ vconfig::child_list vconfig::get_children(const std::string& key) const
for (const config::any_child &child : cfg_->all_children_range())
{
if (child.key == key) {
res.push_back(vconfig(child.cfg, cache_));
res.push_back(vconfig(child.cfg, cache_, *variables_));
} else if (child.key == "insert_tag") {
vconfig insert_cfg(child.cfg);
vconfig insert_cfg(child.cfg, *variables_);
if(insert_cfg["name"] == key)
{
try
{
config::const_child_itors range = as_nonempty_range(insert_cfg["variable"]);
config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
for (const config& ch : range)
{
res.push_back(vconfig(ch, true));
res.push_back(vconfig(ch, true, variables_));
}
}
catch(const invalid_variablename_exception&)
@ -226,12 +262,12 @@ std::size_t vconfig::count_children(const std::string& key) const
if (child.key == key) {
n++;
} else if (child.key == "insert_tag") {
vconfig insert_cfg(child.cfg);
vconfig insert_cfg(child.cfg, *variables_);
if(insert_cfg["name"] == key)
{
try
{
config::const_child_itors range = as_nonempty_range(insert_cfg["variable"]);
config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
n += range.size();
}
catch(const invalid_variablename_exception&)
@ -252,17 +288,17 @@ std::size_t vconfig::count_children(const std::string& key) const
vconfig vconfig::child(const std::string& key) const
{
if (const config &natural = cfg_->child(key)) {
return vconfig(natural, cache_);
return vconfig(natural, cache_, *variables_);
}
for (const config &ins : cfg_->child_range("insert_tag"))
{
vconfig insert_cfg(ins);
vconfig insert_cfg(ins, *variables_);
if(insert_cfg["name"] == key)
{
try
{
config::const_child_itors range = as_nonempty_range(insert_cfg["variable"]);
return vconfig(range.front(), true);
config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
return vconfig(range.front(), true, variables_);
}
catch(const invalid_variablename_exception&)
{
@ -283,7 +319,7 @@ bool vconfig::has_child(const std::string& key) const
}
for (const config &ins : cfg_->child_range("insert_tag"))
{
vconfig insert_cfg(ins);
vconfig insert_cfg(ins, *variables_);
if(insert_cfg["name"] == key) {
return true;
}
@ -295,16 +331,17 @@ namespace {
struct vconfig_expand_visitor : boost::static_visitor<void>
{
config::attribute_value &result;
const variable_set& vars;
vconfig_expand_visitor(config::attribute_value &r): result(r) {}
vconfig_expand_visitor(config::attribute_value &r, const variable_set& vars): result(r), vars(vars) {}
template<typename T> void operator()(const T&) const {}
void operator()(const std::string &s) const
{
result = utils::interpolate_variables_into_string(s, *(resources::gamedata));
result = utils::interpolate_variables_into_string(s, vars);
}
void operator()(const t_string &s) const
{
result = utils::interpolate_variables_into_tstring(s, *(resources::gamedata));
result = utils::interpolate_variables_into_tstring(s, vars);
}
};
}//unnamed namespace
@ -312,37 +349,32 @@ namespace {
config::attribute_value vconfig::expand(const std::string &key) const
{
config::attribute_value val = (*cfg_)[key];
if (resources::gamedata)
val.apply_visitor(vconfig_expand_visitor(val));
val.apply_visitor(vconfig_expand_visitor(val, *variables_));
return val;
}
vconfig::attribute_iterator::reference vconfig::attribute_iterator::operator*() const
{
config::attribute val = *i_;
if(resources::gamedata) {
val.second.apply_visitor(vconfig_expand_visitor(val.second));
}
val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
return val;
}
vconfig::attribute_iterator::pointer vconfig::attribute_iterator::operator->() const
{
config::attribute val = *i_;
if(resources::gamedata) {
val.second.apply_visitor(vconfig_expand_visitor(val.second));
}
val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
pointer_proxy p {val};
return p;
}
vconfig::all_children_iterator::all_children_iterator(const Itor &i) :
i_(i), inner_index_(0), cache_()
vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars) :
i_(i), inner_index_(0), cache_(), variables_(&vars)
{
}
vconfig::all_children_iterator::all_children_iterator(const Itor &i, const std::shared_ptr<const config> & cache) :
i_(i), inner_index_(0), cache_(cache)
vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache) :
i_(i), inner_index_(0), cache_(cache), variables_(&vars)
{
}
@ -352,7 +384,7 @@ vconfig::all_children_iterator& vconfig::all_children_iterator::operator++()
{
try
{
variable_access_const vinfo = resources::gamedata->get_variable_access_read(vconfig(i_->cfg)["variable"]);
variable_access_const vinfo = variables_->get_variable_access_read(vconfig(i_->cfg, *variables_)["variable"]);
config::const_child_itors range = vinfo.as_array();
@ -413,7 +445,7 @@ std::string vconfig::all_children_iterator::get_key() const
{
const std::string &key = i_->key;
if (inner_index_ >= 0 && key == "insert_tag") {
return vconfig(i_->cfg)["name"];
return vconfig(i_->cfg, *variables_)["name"];
}
return key;
}
@ -424,17 +456,17 @@ vconfig vconfig::all_children_iterator::get_child() const
{
try
{
config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg)["variable"]);
config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg, *variables_)["variable"], *variables_);
range.advance_begin(inner_index_);
return vconfig(range.front(), true);
return vconfig(range.front(), true, variables_);
}
catch(const invalid_variablename_exception&)
{
return empty_vconfig();
}
}
return vconfig(i_->cfg, cache_);
return vconfig(i_->cfg, cache_, *variables_);
}
bool vconfig::all_children_iterator::operator==(const all_children_iterator &i) const
@ -444,12 +476,12 @@ bool vconfig::all_children_iterator::operator==(const all_children_iterator &i)
vconfig::all_children_iterator vconfig::ordered_begin() const
{
return all_children_iterator(cfg_->ordered_begin(), cache_);
return all_children_iterator(cfg_->ordered_begin(), *variables_, cache_);
}
vconfig::all_children_iterator vconfig::ordered_end() const
{
return all_children_iterator(cfg_->ordered_end(), cache_);
return all_children_iterator(cfg_->ordered_end(), *variables_, cache_);
}
scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :

View file

@ -18,6 +18,7 @@
#include "config.hpp"
#include "map/location.hpp"
#include "variable_info.hpp"
#include <utility>
@ -28,6 +29,7 @@ class config_variable_set : public variable_set {
public:
config_variable_set(const config& cfg) : cfg_(cfg) {}
virtual config::attribute_value get_variable_const(const std::string &id) const;
virtual variable_access_const get_variable_access_read(const std::string& varname) const;
};
/**
@ -55,13 +57,18 @@ private:
vconfig();
vconfig(const config & cfg, const std::shared_ptr<const config> & cache);
vconfig(const config& cfg, const std::shared_ptr<const config> & cache, const variable_set& variables);
public:
/// Constructor from a config.
/// Equivalent to vconfig(cfg, false).
/// Do not use if the vconfig will persist after @a cfg is destroyed!
explicit vconfig(const config &cfg) : cache_(), cfg_(&cfg) {}
explicit vconfig(config &&cfg) : cache_(new config(std::move(cfg))), cfg_(cache_.get()) { }
vconfig(const config &cfg, bool manage_memory);
explicit vconfig(const config &cfg);// : cache_(), cfg_(&cfg) {}
explicit vconfig(config &&cfg);// : cache_(new config(std::move(cfg))), cfg_(cache_.get()) { }
// Construct a vconfig referencing a non-default set of variables.
// Note that the vconfig does NOT take ownership of these variables,
// so you need to make sure that their scope encloses the vconfig's scope!
vconfig(const config& cfg, const variable_set& variables);
vconfig(const config &cfg, bool manage_memory, const variable_set* variables = nullptr);
~vconfig();
static vconfig empty_vconfig(); // Valid to dereference. Contains nothing
@ -109,13 +116,13 @@ public:
typedef const pointer_proxy pointer;
typedef const config::attribute reference;
typedef config::const_attribute_iterator Itor;
explicit attribute_iterator(const Itor &i): i_(i) {}
explicit attribute_iterator(const Itor &i, const variable_set& vars): i_(i), variables_(&vars) {}
attribute_iterator &operator++() { ++i_; return *this; }
attribute_iterator operator++(int) { return attribute_iterator(i_++); }
attribute_iterator operator++(int) { return attribute_iterator(i_++, *variables_); }
attribute_iterator &operator--() { --i_; return *this; }
attribute_iterator operator--(int) { return attribute_iterator(i_--); }
attribute_iterator operator--(int) { return attribute_iterator(i_--, *variables_); }
reference operator*() const;
pointer operator->() const;
@ -125,11 +132,12 @@ public:
private:
Itor i_;
const variable_set* variables_;
};
boost::iterator_range<attribute_iterator> attribute_range() {
config::const_attr_itors range = cfg_->attribute_range();
return boost::make_iterator_range(attribute_iterator(range.begin()), attribute_iterator(range.end()));
return boost::make_iterator_range(attribute_iterator(range.begin(), *variables_), attribute_iterator(range.end(), *variables_));
}
struct all_children_iterator
@ -142,8 +150,8 @@ public:
typedef const pointer_proxy pointer;
typedef const value_type reference;
typedef config::const_all_children_iterator Itor;
explicit all_children_iterator(const Itor &i);
all_children_iterator(const Itor &i, const std::shared_ptr<const config> & cache);
explicit all_children_iterator(const Itor &i, const variable_set& vars);
all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache);
all_children_iterator& operator++();
all_children_iterator operator++(int);
@ -176,6 +184,7 @@ public:
*/
int inner_index_;
std::shared_ptr<const config> cache_;
const variable_set* variables_;
};
struct recursion_error : public config::error {
@ -198,6 +207,7 @@ private:
mutable std::shared_ptr<const config> cache_;
/// Used to access our config (original or copy, as appropriate).
mutable const config* cfg_;
const variable_set* variables_;
static const config default_empty_config;
};

View file

@ -149,3 +149,10 @@ using variable_access_throw = variable_info_mutable<variable_info_implementatio
* the policy as const here. This allows the use of const_clone.
*/
using variable_access_const = variable_info<const variable_info_implementation::vi_policy_const>;
class variable_set {
public:
virtual ~variable_set() {}
virtual config::attribute_value get_variable_const(const std::string &id) const = 0;
virtual variable_access_const get_variable_access_read(const std::string& varname) const = 0;
};