Commit 69504271 authored by Dominik Charousset's avatar Dominik Charousset

Refactor parsing of config options

parent baa4241b
......@@ -31,6 +31,7 @@
#include "caf/atom.hpp"
#include "caf/detail/bounds_checker.hpp"
#include "caf/detail/move_if_not_ptr.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
......@@ -75,6 +76,8 @@ public:
using variant_type = detail::tl_apply_t<types, variant>;
using parse_state = detail::parse_state;
// -- constructors, destructors, and assignment operators --------------------
config_value() = default;
......@@ -230,6 +233,14 @@ struct default_config_value_access {
static T get(const config_value& x) {
return caf::get<T>(x.get_data());
}
static T convert(T x) {
return x;
}
static void parse(config_value::parse_state& ps, T& x) {
detail::parse(ps, x);
}
};
struct config_value_access_unspecialized {};
......@@ -266,7 +277,7 @@ enum class select_config_value_hint {
template <class T>
constexpr select_config_value_hint select_config_value_oracle() {
return std::is_integral<T>::value
return std::is_integral<T>::value && !std::is_same<T, bool>::value
? select_config_value_hint::is_integral
: (detail::is_map_like<T>::value
? select_config_value_hint::is_map
......@@ -288,8 +299,8 @@ struct select_config_value_access {
};
template <class T>
using select_config_value_access_t =
typename select_config_value_access<T>::type;
using select_config_value_access_t = typename select_config_value_access<
T>::type;
template <>
struct sum_type_access<config_value> {
......@@ -395,6 +406,14 @@ struct select_config_value_access<T, select_config_value_hint::is_integral> {
CAF_ASSERT(res != none);
return *res;
}
static T convert(T x) {
return x;
}
static void parse(config_value::parse_state& ps, T& x) {
detail::parse(ps, x);
}
};
};
......@@ -444,6 +463,34 @@ struct select_config_value_access<T, select_config_value_hint::is_list> {
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
static config_value::list convert(const list_type& xs) {
config_value::list result;
for (const auto& x : xs)
result.emplace_back(value_trait::convert(x));
return result;
}
static void parse(config_value::parse_state& ps, T& xs) {
config_value::dictionary result;
bool has_open_token = ps.consume('[');
do {
if (has_open_token && ps.consume(']')) {
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
return;
}
value_type tmp;
value_trait::parse(ps, tmp);
if (ps.code > pec::trailing_character)
return;
xs.insert(xs.end(), std::move(tmp));
} while (ps.consume(','));
if (has_open_token && !ps.consume(']'))
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
};
};
......@@ -496,28 +543,47 @@ struct select_config_value_access<T, select_config_value_hint::is_map> {
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
static void parse(config_value::parse_state& ps, map_type& xs) {
detail::parse(ps, xs);
}
static config_value::dictionary convert(const map_type& xs) {
config_value::dictionary result;
for (const auto& x : xs)
result.emplace(x.first, mapped_trait::convert(x.second));
return result;
}
};
};
template <>
struct config_value_access<float> {
static std::string type_name() {
static inline std::string type_name() {
return "real32";
}
static bool is(const config_value& x) {
static inline bool is(const config_value& x) {
return holds_alternative<double>(x.get_data());
}
static optional<float> get_if(const config_value* x) {
static inline optional<float> get_if(const config_value* x) {
if (auto res = caf::get_if<double>(&(x->get_data())))
return static_cast<float>(*res);
return none;
}
static float get(const config_value& x) {
static inline float get(const config_value& x) {
return static_cast<float>(caf::get<double>(x.get_data()));
}
static inline double convert(float x) {
return x;
}
static inline void parse(config_value::parse_state& ps, float& x) {
detail::parse(ps, x);
}
};
/// Implements automagic unboxing of `std::tuple<Ts...>` from a heterogeneous
......@@ -561,6 +627,16 @@ struct config_value_access<std::tuple<Ts...>> {
CAF_RAISE_ERROR("invalid type found");
}
static config_value::list convert(const tuple_type& xs) {
config_value::list result;
rec_convert(result, xs, detail::int_token<0>(), detail::type_list<Ts...>());
return result;
}
static void parse(config_value::parse_state& ps, tuple_type& xs) {
rec_parse(ps, xs, detail::int_token<0>(), detail::type_list<Ts...>());
}
private:
template <int Pos>
static void rec_name(std::string&, bool, detail::int_token<Pos>,
......@@ -610,6 +686,39 @@ private:
}
return false;
}
template <int Pos>
static void rec_convert(config_value::list&, const tuple_type&,
detail::int_token<Pos>, detail::type_list<>) {
// nop
}
template <int Pos, class U, class... Us>
static void rec_convert(config_value::list& result, const tuple_type& xs,
detail::int_token<Pos>, detail::type_list<U, Us...>) {
using trait = select_config_value_access_t<U>;
result.emplace_back(trait::convert(std::get<Pos>(xs)));
return rec_convert(result, xs, detail::int_token<Pos + 1>(),
detail::type_list<Us...>());
}
template <int Pos>
static void rec_parse(config_value::parse_state&, tuple_type&,
detail::int_token<Pos>, detail::type_list<>) {
// nop
}
template <int Pos, class U, class... Us>
static void rec_parse(config_value::parse_state& ps, tuple_type& xs,
detail::int_token<Pos>, detail::type_list<U, Us...>) {
using trait = select_config_value_access_t<U>;
trait::parse(std::get<Pos>(xs));
if (ps.code > pec::trailing_character)
return;
if (sizeof...(Us) > 0 && !ps.consume(','))
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
rec_parse(ps, xs, detail::int_token<Pos + 1>(), detail::type_list<Us...>());
}
};
// -- SumType-like access of dictionary values ---------------------------------
......
......@@ -21,6 +21,7 @@
#include <cctype>
#include <cstdint>
#include "caf/error.hpp"
#include "caf/pec.hpp"
namespace caf {
......@@ -90,6 +91,11 @@ struct state {
}
return false;
}
error make_error(pec code) {
return caf::make_error(code, static_cast<size_t>(line),
static_cast<size_t>(column));
}
};
} // namespace parser
......
......@@ -53,15 +53,16 @@ config_value get_impl(const void* ptr) {
template <class T>
expected<config_value> parse_impl(T* ptr, string_view str) {
if (!ptr) {
T tmp;
if (auto err = parse(str, tmp))
return err;
config_value result{std::move(tmp)};
if (!holds_alternative<T>(result))
return pec::type_mismatch;
if (ptr != nullptr)
*ptr = get<T>(result);
return std::move(result);
return parse_impl(&tmp, str);
}
using trait = select_config_value_access_t<T>;
config_value::parse_state ps{str.begin(), str.end()};
trait::parse(ps, *ptr);
if (ps.code != pec::success)
return ps.make_error(ps.code);
return config_value{trait::convert(*ptr)};
}
expected<config_value> parse_impl(std::string* ptr, string_view str);
......
......@@ -65,6 +65,8 @@ enum class pec : uint8_t {
missing_argument,
/// Stopped because the key of a category was taken.
illegal_category,
/// Stopped at an unexpected field name while reading a user-defined type.
invalid_field_name,
};
/// Returns an error object from given error code.
......
......@@ -185,7 +185,6 @@ CAF_TEST(type timespan) {
CAF_TEST(lists) {
using int_list = std::vector<int>;
CAF_CHECK_EQUAL(read<int_list>(""), int_list({}));
CAF_CHECK_EQUAL(read<int_list>("[]"), int_list({}));
CAF_CHECK_EQUAL(read<int_list>("1, 2, 3"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>("[1, 2, 3]"), int_list({1, 2, 3}));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment