231 lines
6.7 KiB
C++
231 lines
6.7 KiB
C++
/*
|
|
Copyright (C) 2003 by David White <dave@whitevine.net>
|
|
Copyright (C) 2005 - 2018 by Philippe Plantier <ayin@anathas.org>
|
|
|
|
Part of the Battle for Wesnoth Project https://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 "variable_info.hpp"
|
|
#include "variable_info_private.hpp"
|
|
|
|
#include <utility>
|
|
|
|
namespace variable_info_implementation
|
|
{
|
|
/**
|
|
* Helper function to apply the result of a specified visitor to a variable_info object.
|
|
*
|
|
* @tparam V Visitor type.
|
|
* @tparam T Visitor argument parameter pack.
|
|
*
|
|
* @param state Info state (the actual variable data).
|
|
* @param args Arguments to forward to visitor constructor.
|
|
*
|
|
* @returns Visitor output in its specified type.
|
|
* @throws std::range_error If @a state has an invalid type_ field.
|
|
*/
|
|
template<typename V, typename... T>
|
|
typename V::result_t apply_visitor(typename V::param_t state, T&&... args)
|
|
{
|
|
static_assert(std::is_base_of<
|
|
info_visitor_base<
|
|
typename V::result_t,
|
|
std::remove_reference_t<typename V::param_t>>,
|
|
V>::value, "Invalid visitor type.");
|
|
|
|
// Create the visitor.
|
|
V visitor(std::forward<T>(args)...);
|
|
|
|
switch(state.type_) {
|
|
case state_start:
|
|
return visitor.from_start(state);
|
|
case state_named:
|
|
return visitor.from_named(state);
|
|
case state_indexed:
|
|
return visitor.from_indexed(state);
|
|
case state_temporary:
|
|
return visitor.from_temporary(state);
|
|
}
|
|
|
|
throw std::range_error("Failed to convert the TVisitor::param_t type");
|
|
}
|
|
} // end namespace variable_info_implementation
|
|
|
|
using namespace variable_info_implementation;
|
|
|
|
template<typename V>
|
|
variable_info<V>::variable_info(const std::string& varname, maybe_const_t<config, V>& vars) noexcept
|
|
: name_(varname)
|
|
, state_(vars)
|
|
, valid_(true)
|
|
{
|
|
try {
|
|
calculate_value();
|
|
} catch(const invalid_variablename_exception&) {
|
|
valid_ = false;
|
|
}
|
|
}
|
|
|
|
template<typename V>
|
|
void variable_info<V>::calculate_value()
|
|
{
|
|
std::size_t previous_index = 0, name_size = name_.size();
|
|
|
|
for(std::size_t loop_index = 0; loop_index < name_size; loop_index++) {
|
|
switch(name_[loop_index]) {
|
|
case '.':
|
|
case '[':
|
|
/* '.' and '[' mark the end of a string key.
|
|
* The result is obviously that '.' and '[' are
|
|
* treated equally so 'aaa.9].bbbb[zzz.uu.7]'
|
|
* is interpreted as 'aaa[9].bbbb.zzz.uu[7]'
|
|
* Use is_valid_variable function for stricter variable name checking.
|
|
*/
|
|
apply_visitor<get_variable_key_visitor<V>>(
|
|
state_, name_.substr(previous_index, loop_index - previous_index));
|
|
|
|
previous_index = loop_index + 1;
|
|
break;
|
|
case ']':
|
|
// ']' marks the end of an integer key.
|
|
apply_visitor<get_variable_index_visitor<V>>(state_, parse_index(&name_[previous_index]));
|
|
|
|
// After ']' we always expect a '.' or the end of the string
|
|
// Ignore the next char which is a '.'
|
|
loop_index++;
|
|
if(loop_index < name_.length() && name_[loop_index] != '.') {
|
|
throw invalid_variablename_exception();
|
|
}
|
|
|
|
previous_index = loop_index + 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(previous_index != name_.length() + 1) {
|
|
// The string didn't end with ']'
|
|
// In this case we still didn't add the key behind the last '.'
|
|
apply_visitor<get_variable_key_visitor<V>>(state_, name_.substr(previous_index));
|
|
}
|
|
}
|
|
|
|
template<typename V>
|
|
bool variable_info<V>::explicit_index() const
|
|
{
|
|
throw_on_invalid();
|
|
return state_.type_ == state_start || state_.type_ == state_indexed;
|
|
}
|
|
|
|
template<typename V>
|
|
maybe_const_t<config::attribute_value, V>& variable_info<V>::as_scalar() const
|
|
{
|
|
throw_on_invalid();
|
|
return apply_visitor<as_scalar_visitor<V>>(state_);
|
|
}
|
|
|
|
template<typename V>
|
|
maybe_const_t<config, V>& variable_info<V>::as_container() const
|
|
{
|
|
throw_on_invalid();
|
|
return apply_visitor<as_container_visitor<V>>(state_);
|
|
}
|
|
|
|
template<typename V>
|
|
maybe_const_t<config::child_itors, V> variable_info<V>::as_array() const
|
|
{
|
|
throw_on_invalid();
|
|
return apply_visitor<as_array_visitor<V>>(state_);
|
|
}
|
|
|
|
template<typename V>
|
|
void variable_info<V>::throw_on_invalid() const
|
|
{
|
|
if(!valid_) {
|
|
throw invalid_variablename_exception();
|
|
}
|
|
}
|
|
|
|
template<typename V>
|
|
std::string variable_info<V>::get_error_message() const
|
|
{
|
|
return V::error_message(name_);
|
|
}
|
|
|
|
template<typename V>
|
|
bool variable_info<V>::exists_as_attribute() const
|
|
{
|
|
throw_on_invalid();
|
|
return (state_.type_ == state_temporary)
|
|
|| ((state_.type_ == state_named) && state_.child_->has_attribute(state_.key_));
|
|
}
|
|
|
|
template<typename V>
|
|
bool variable_info<V>::exists_as_container() const
|
|
{
|
|
throw_on_invalid();
|
|
return apply_visitor<exists_as_container_visitor<V>>(state_);
|
|
}
|
|
|
|
template<typename V>
|
|
void variable_info_mutable<V>::clear(bool only_tables) const
|
|
{
|
|
this->throw_on_invalid();
|
|
return apply_visitor<clear_value_visitor<V>>(this->state_, only_tables);
|
|
}
|
|
|
|
/**
|
|
* In order to allow the appropriate 'children' argument to forward through to the appropriate
|
|
* ctor of type T in as_range_visitor_base via apply_visitor, we need to specify the argument
|
|
* type as an rvalue reference to lvalue reference in order to collapse to an lvalue reference.
|
|
*
|
|
* Using a convenient template alias for convenience.
|
|
*/
|
|
template<typename V, typename T>
|
|
using range_visitor_wrapper = as_range_visitor_base<V, T, std::vector<config>&>;
|
|
|
|
template<typename V>
|
|
config::child_itors variable_info_mutable<V>::append_array(std::vector<config> children) const
|
|
{
|
|
this->throw_on_invalid();
|
|
return apply_visitor<range_visitor_wrapper<V, append_range_h>>(this->state_, children);
|
|
}
|
|
|
|
template<typename V>
|
|
config::child_itors variable_info_mutable<V>::insert_array(std::vector<config> children) const
|
|
{
|
|
this->throw_on_invalid();
|
|
return apply_visitor<range_visitor_wrapper<V, insert_range_h>>(this->state_, children);
|
|
}
|
|
|
|
template<typename V>
|
|
config::child_itors variable_info_mutable<V>::replace_array(std::vector<config> children) const
|
|
{
|
|
this->throw_on_invalid();
|
|
return apply_visitor<range_visitor_wrapper<V, replace_range_h>>(this->state_, children);
|
|
}
|
|
|
|
template<typename V>
|
|
void variable_info_mutable<V>::merge_array(std::vector<config> children) const
|
|
{
|
|
this->throw_on_invalid();
|
|
apply_visitor<range_visitor_wrapper<V, merge_range_h>>(this->state_, children);
|
|
}
|
|
|
|
// Force compilation of the following template instantiations
|
|
template class variable_info<const vi_policy_const>;
|
|
template class variable_info<vi_policy_create>;
|
|
template class variable_info<vi_policy_throw>;
|
|
|
|
template class variable_info_mutable<vi_policy_create>;
|
|
template class variable_info_mutable<vi_policy_throw>;
|