Commit 69504271 authored by Dominik Charousset's avatar Dominik Charousset

Refactor parsing of config options

parent baa4241b
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/bounds_checker.hpp" #include "caf/detail/bounds_checker.hpp"
#include "caf/detail/move_if_not_ptr.hpp" #include "caf/detail/move_if_not_ptr.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/dictionary.hpp" #include "caf/dictionary.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -75,6 +76,8 @@ public: ...@@ -75,6 +76,8 @@ public:
using variant_type = detail::tl_apply_t<types, variant>; using variant_type = detail::tl_apply_t<types, variant>;
using parse_state = detail::parse_state;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
config_value() = default; config_value() = default;
...@@ -230,6 +233,14 @@ struct default_config_value_access { ...@@ -230,6 +233,14 @@ struct default_config_value_access {
static T get(const config_value& x) { static T get(const config_value& x) {
return caf::get<T>(x.get_data()); 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 {}; struct config_value_access_unspecialized {};
...@@ -266,7 +277,7 @@ enum class select_config_value_hint { ...@@ -266,7 +277,7 @@ enum class select_config_value_hint {
template <class T> template <class T>
constexpr select_config_value_hint select_config_value_oracle() { 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 ? select_config_value_hint::is_integral
: (detail::is_map_like<T>::value : (detail::is_map_like<T>::value
? select_config_value_hint::is_map ? select_config_value_hint::is_map
...@@ -288,8 +299,8 @@ struct select_config_value_access { ...@@ -288,8 +299,8 @@ struct select_config_value_access {
}; };
template <class T> template <class T>
using select_config_value_access_t = using select_config_value_access_t = typename select_config_value_access<
typename select_config_value_access<T>::type; T>::type;
template <> template <>
struct sum_type_access<config_value> { struct sum_type_access<config_value> {
...@@ -395,6 +406,14 @@ struct select_config_value_access<T, select_config_value_hint::is_integral> { ...@@ -395,6 +406,14 @@ struct select_config_value_access<T, select_config_value_hint::is_integral> {
CAF_ASSERT(res != none); CAF_ASSERT(res != none);
return *res; 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> { ...@@ -444,6 +463,34 @@ struct select_config_value_access<T, select_config_value_hint::is_list> {
CAF_RAISE_ERROR("invalid type found"); CAF_RAISE_ERROR("invalid type found");
return std::move(*result); 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> { ...@@ -496,28 +543,47 @@ struct select_config_value_access<T, select_config_value_hint::is_map> {
CAF_RAISE_ERROR("invalid type found"); CAF_RAISE_ERROR("invalid type found");
return std::move(*result); 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 <> template <>
struct config_value_access<float> { struct config_value_access<float> {
static std::string type_name() { static inline std::string type_name() {
return "real32"; return "real32";
} }
static bool is(const config_value& x) { static inline bool is(const config_value& x) {
return holds_alternative<double>(x.get_data()); 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()))) if (auto res = caf::get_if<double>(&(x->get_data())))
return static_cast<float>(*res); return static_cast<float>(*res);
return none; 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())); 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 /// Implements automagic unboxing of `std::tuple<Ts...>` from a heterogeneous
...@@ -561,6 +627,16 @@ struct config_value_access<std::tuple<Ts...>> { ...@@ -561,6 +627,16 @@ struct config_value_access<std::tuple<Ts...>> {
CAF_RAISE_ERROR("invalid type found"); 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: private:
template <int Pos> template <int Pos>
static void rec_name(std::string&, bool, detail::int_token<Pos>, static void rec_name(std::string&, bool, detail::int_token<Pos>,
...@@ -610,6 +686,39 @@ private: ...@@ -610,6 +686,39 @@ private:
} }
return false; 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 --------------------------------- // -- SumType-like access of dictionary values ---------------------------------
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <cctype> #include <cctype>
#include <cstdint> #include <cstdint>
#include "caf/error.hpp"
#include "caf/pec.hpp" #include "caf/pec.hpp"
namespace caf { namespace caf {
...@@ -90,6 +91,11 @@ struct state { ...@@ -90,6 +91,11 @@ struct state {
} }
return false; return false;
} }
error make_error(pec code) {
return caf::make_error(code, static_cast<size_t>(line),
static_cast<size_t>(column));
}
}; };
} // namespace parser } // namespace parser
......
...@@ -53,15 +53,16 @@ config_value get_impl(const void* ptr) { ...@@ -53,15 +53,16 @@ config_value get_impl(const void* ptr) {
template <class T> template <class T>
expected<config_value> parse_impl(T* ptr, string_view str) { expected<config_value> parse_impl(T* ptr, string_view str) {
T tmp; if (!ptr) {
if (auto err = parse(str, tmp)) T tmp;
return err; return parse_impl(&tmp, str);
config_value result{std::move(tmp)}; }
if (!holds_alternative<T>(result)) using trait = select_config_value_access_t<T>;
return pec::type_mismatch; config_value::parse_state ps{str.begin(), str.end()};
if (ptr != nullptr) trait::parse(ps, *ptr);
*ptr = get<T>(result); if (ps.code != pec::success)
return std::move(result); return ps.make_error(ps.code);
return config_value{trait::convert(*ptr)};
} }
expected<config_value> parse_impl(std::string* ptr, string_view str); expected<config_value> parse_impl(std::string* ptr, string_view str);
......
...@@ -65,6 +65,8 @@ enum class pec : uint8_t { ...@@ -65,6 +65,8 @@ enum class pec : uint8_t {
missing_argument, missing_argument,
/// Stopped because the key of a category was taken. /// Stopped because the key of a category was taken.
illegal_category, 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. /// Returns an error object from given error code.
......
...@@ -185,7 +185,6 @@ CAF_TEST(type timespan) { ...@@ -185,7 +185,6 @@ CAF_TEST(type timespan) {
CAF_TEST(lists) { CAF_TEST(lists) {
using int_list = std::vector<int>; 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>("[]"), 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}));
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