Commit 64324ccf authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Integrate new parser infrastructure

parent 042548ff
...@@ -37,6 +37,7 @@ set(LIBCAF_CORE_SRCS ...@@ -37,6 +37,7 @@ set(LIBCAF_CORE_SRCS
src/blocking_behavior.cpp src/blocking_behavior.cpp
src/concatenated_tuple.cpp src/concatenated_tuple.cpp
src/config_option.cpp src/config_option.cpp
src/config_option_set.cpp
src/config_value.cpp src/config_value.cpp
src/decorated_tuple.cpp src/decorated_tuple.cpp
src/default_attachable.cpp src/default_attachable.cpp
...@@ -47,7 +48,6 @@ set(LIBCAF_CORE_SRCS ...@@ -47,7 +48,6 @@ set(LIBCAF_CORE_SRCS
src/downstream_messages.cpp src/downstream_messages.cpp
src/duration.cpp src/duration.cpp
src/dynamic_message_data.cpp src/dynamic_message_data.cpp
src/ec.cpp
src/error.cpp src/error.cpp
src/event_based_actor.cpp src/event_based_actor.cpp
src/execution_unit.cpp src/execution_unit.cpp
...@@ -61,6 +61,7 @@ set(LIBCAF_CORE_SRCS ...@@ -61,6 +61,7 @@ set(LIBCAF_CORE_SRCS
src/group_manager.cpp src/group_manager.cpp
src/group_module.cpp src/group_module.cpp
src/inbound_path.cpp src/inbound_path.cpp
src/ini_consumer.cpp
src/invoke_result_visitor.cpp src/invoke_result_visitor.cpp
src/local_actor.cpp src/local_actor.cpp
src/logger.cpp src/logger.cpp
...@@ -77,6 +78,7 @@ set(LIBCAF_CORE_SRCS ...@@ -77,6 +78,7 @@ set(LIBCAF_CORE_SRCS
src/node_id.cpp src/node_id.cpp
src/outbound_path.cpp src/outbound_path.cpp
src/parse_ini.cpp src/parse_ini.cpp
src/pec.cpp
src/pretty_type_name.cpp src/pretty_type_name.cpp
src/private_thread.cpp src/private_thread.cpp
src/proxy_registry.cpp src/proxy_registry.cpp
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "caf/thread_hook.hpp" #include "caf/thread_hook.hpp"
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
#include "caf/config_option_set.hpp"
#include "caf/actor_factory.hpp" #include "caf/actor_factory.hpp"
#include "caf/is_typed_actor.hpp" #include "caf/is_typed_actor.hpp"
#include "caf/type_erased_value.hpp" #include "caf/type_erased_value.hpp"
...@@ -74,31 +75,37 @@ public: ...@@ -74,31 +75,37 @@ public:
using error_renderer_map = hash_map<atom_value, error_renderer>; using error_renderer_map = hash_map<atom_value, error_renderer>;
using option_ptr = std::unique_ptr<config_option>;
using option_vector = std::vector<option_ptr>;
using group_module_factory = std::function<group_module* ()>; using group_module_factory = std::function<group_module* ()>;
using group_module_factory_vector = std::vector<group_module_factory>; using group_module_factory_vector = std::vector<group_module_factory>;
using config_map = std::map<std::string, config_value::dictionary>;
using string_list = std::vector<std::string>;
// -- nested classes --------------------------------------------------------- // -- nested classes ---------------------------------------------------------
using named_actor_config_map = hash_map<std::string, named_actor_config>; using named_actor_config_map = hash_map<std::string, named_actor_config>;
class opt_group { class opt_group {
public: public:
opt_group(option_vector& xs, const char* category); opt_group(config_option_set& xs, const char* category);
template <class T>
opt_group& add(T& storage, const char* name, const char* description) {
xs_.add(storage, category_, name, description);
return *this;
}
template <class T> template <class T>
opt_group& add(T& storage, const char* name, const char* explanation) { opt_group& add(const char* name, const char* description) {
xs_.emplace_back(make_config_option(storage, cat_, name, explanation)); xs_.add<T>(category_, name, description);
return *this; return *this;
} }
private: private:
option_vector& xs_; config_option_set& xs_;
const char* cat_; const char* category_;
}; };
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
...@@ -112,18 +119,29 @@ public: ...@@ -112,18 +119,29 @@ public:
actor_system_config(const actor_system_config&) = delete; actor_system_config(const actor_system_config&) = delete;
actor_system_config& operator=(const actor_system_config&) = delete; actor_system_config& operator=(const actor_system_config&) = delete;
// -- properties -------------------------------------------------------------
/// @private
std::map<std::string, config_value::dictionary> content;
/// Sets a config by using its INI name `config_name` to `config_value`.
template <class T>
actor_system_config& set(const char* name, T&& value) {
return set_impl(name, config_value{std::forward<T>(value)});
}
// -- modifiers -------------------------------------------------------------- // -- modifiers --------------------------------------------------------------
/// Parses `args` as tuple of strings containing CLI options
/// and `ini_stream` as INI formatted input stream.
actor_system_config& parse(string_list args, std::istream& ini);
/// Parses `args` as tuple of strings containing CLI options and tries to /// Parses `args` as tuple of strings containing CLI options and tries to
/// open `ini_file_cstr` as INI formatted config file. The parsers tries to /// open `ini_file_cstr` as INI formatted config file. The parsers tries to
/// open `caf-application.ini` if `ini_file_cstr` is `nullptr`. /// open `caf-application.ini` if `ini_file_cstr` is `nullptr`.
actor_system_config& parse(message& args, actor_system_config& parse(string_list args,
const char* ini_file_cstr = nullptr); const char* ini_file_cstr = nullptr);
/// Parses `args` as tuple of strings containing CLI options
/// and `ini_stream` as INI formatted input stream.
actor_system_config& parse(message& args, std::istream& ini);
/// Parses the CLI options `{argc, argv}` and /// Parses the CLI options `{argc, argv}` and
/// `ini_stream` as INI formatted input stream. /// `ini_stream` as INI formatted input stream.
actor_system_config& parse(int argc, char** argv, std::istream& ini); actor_system_config& parse(int argc, char** argv, std::istream& ini);
...@@ -134,6 +152,11 @@ public: ...@@ -134,6 +152,11 @@ public:
actor_system_config& parse(int argc, char** argv, actor_system_config& parse(int argc, char** argv,
const char* ini_file_cstr = nullptr); const char* ini_file_cstr = nullptr);
actor_system_config&
parse(message& args, const char* ini_file_cstr = nullptr) CAF_DEPRECATED;
actor_system_config& parse(message& args, std::istream& ini) CAF_DEPRECATED;
/// Allows other nodes to spawn actors created by `fun` /// Allows other nodes to spawn actors created by `fun`
/// dynamically by using `name` as identifier. /// dynamically by using `name` as identifier.
/// @experimental /// @experimental
...@@ -237,12 +260,6 @@ public: ...@@ -237,12 +260,6 @@ public:
return *this; return *this;
} }
/// Sets a config by using its INI name `config_name` to `config_value`.
template <class T>
actor_system_config& set(const char* cn, T&& x) {
return set_impl(cn, config_value{std::forward<T>(x)});
}
// -- parser and CLI state --------------------------------------------------- // -- parser and CLI state ---------------------------------------------------
/// Stores whether the help text was printed. If set to `true`, the /// Stores whether the help text was printed. If set to `true`, the
...@@ -385,16 +402,6 @@ public: ...@@ -385,16 +402,6 @@ public:
/// default. /// default.
std::string config_file_path; std::string config_file_path;
// -- convenience functions --------------------------------------------------
template <class F>
void for_each_option(F f) const {
const option_vector* all_options[] = { &options_, &custom_options_ };
for (auto& opt_vec : all_options)
for (auto& opt : *opt_vec)
f(*opt);
}
// -- utility for caf-run ---------------------------------------------------- // -- utility for caf-run ----------------------------------------------------
// Config parameter for individual actor types. // Config parameter for individual actor types.
...@@ -405,7 +412,7 @@ public: ...@@ -405,7 +412,7 @@ public:
protected: protected:
virtual std::string make_help_text(const std::vector<message::cli_arg>&); virtual std::string make_help_text(const std::vector<message::cli_arg>&);
option_vector custom_options_; config_option_set custom_options_;
private: private:
template <class T> template <class T>
...@@ -416,15 +423,15 @@ private: ...@@ -416,15 +423,15 @@ private:
&make_type_erased_value<T>); &make_type_erased_value<T>);
} }
actor_system_config& set_impl(const char* cn, config_value cv); actor_system_config& set_impl(const char* name, config_value value);
static std::string render_sec(uint8_t, atom_value, const message&); static std::string render_sec(uint8_t, atom_value, const message&);
static std::string render_exit_reason(uint8_t, atom_value, const message&); static std::string render_exit_reason(uint8_t, atom_value, const message&);
void extract_config_file_path(message& args); void extract_config_file_path(string_list& args);
option_vector options_; config_option_set options_;
}; };
} // namespace caf } // namespace caf
......
...@@ -18,179 +18,75 @@ ...@@ -18,179 +18,75 @@
#pragma once #pragma once
#include <memory>
#include <string> #include <string>
#include <vector>
#include <cstdint>
#include <cstring>
#include <functional>
#include "caf/atom.hpp"
#include "caf/config_value.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/error.hpp"
#include "caf/message.hpp"
#include "caf/static_visitor.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf { #include "caf/fwd.hpp"
extern const char* type_name_visitor_tbl[]; namespace caf {
/// Helper class to generate config readers for different input types. /// Defines a configuration option for the application.
class config_option { class config_option {
public: public:
using config_reader_sink = std::function<bool (size_t, config_value&, // -- member types -----------------------------------------------------------
optional<std::ostream&>)>;
/// Custom vtable-like struct for delegating to type-specific functions.
struct vtbl_type {
error (*check)(const config_value&);
void (*store)(void*, const config_value&);
};
using legal_types = detail::type_list<bool, float, double, std::string, // -- constructors, destructors, and assignment operators --------------------
atom_value, int8_t, uint8_t, int16_t,
uint16_t, int32_t, uint32_t, int64_t,
uint64_t, timespan>;
config_option(const char* cat, const char* nm, const char* expl); /// Constructs a config option.
config_option(std::string category, const char* name, std::string description,
bool is_flag, const vtbl_type& vtbl, void* storage = nullptr);
virtual ~config_option(); config_option(config_option&&) = default;
inline const char* name() const { config_option& operator=(config_option&&) = default;
return name_.c_str();
}
inline char short_name() const { // -- properties -------------------------------------------------------------
return short_name_;
}
inline const char* category() const { inline const std::string& category() const noexcept {
return category_; return category_;
} }
inline const char* explanation() const { /// Returns the
return explanation_; inline const std::string& long_name() const noexcept {
return long_name_;
} }
/// Returns the full name for this config option as "<category>.<long name>". inline const std::string& short_names() const noexcept {
std::string full_name() const; return short_names_;
/// Returns the held value as string.
virtual std::string to_string() const = 0;
/// Returns a sink function for config readers.
virtual config_reader_sink to_sink() = 0;
/// Returns a CLI argument parser.
virtual message::cli_arg to_cli_arg(bool use_caf_prefix = false) = 0;
/// Returns a human-readable type name for the visited type.
class type_name_visitor : public static_visitor<const char*> {
public:
template <class T>
const char* operator()(const T&) const {
static constexpr bool is_int = std::is_integral<T>::value
&& !std::is_same<bool, T>::value;
static constexpr std::integral_constant<bool, is_int> tk{};
static constexpr int index = idx<T>(tk);
static_assert(index >= 0, "illegal type in name visitor");
return type_name_visitor_tbl[static_cast<size_t>(index)];
}
template <class U>
const char* operator()(const std::vector<U>&) {
return "a list";
}
template <class K, class V>
const char* operator()(const std::map<K, V>&) {
return "a dictionary";
}
const char* operator()(const timespan&) {
return "a timespan";
}
private:
// Catches non-integer types.
template <class T>
static constexpr int idx(std::false_type) {
return detail::tl_index_of<legal_types, T>::value;
}
// Catches integer types.
template <class T>
static constexpr int idx(std::true_type) {
using squashed = detail::squashed_int_t<T>;
return detail::tl_index_of<legal_types, squashed>::value;
}
};
protected:
void report_type_error(size_t ln, config_value& x, const char* expected,
optional<std::ostream&> out);
private:
const char* category_;
std::string name_;
const char* explanation_;
char short_name_;
};
template <class T>
class config_option_impl : public config_option {
public:
config_option_impl(T& ref, const char* ctg, const char* nm, const char* xp)
: config_option(ctg, nm, xp),
ref_(ref) {
// nop
} }
std::string to_string() const override { inline const std::string& description() const noexcept {
return deep_to_string(ref_); return description_;
} }
message::cli_arg to_cli_arg(bool use_caf_prefix) override { inline bool is_flag() const noexcept {
std::string argname; return is_flag_;
if (use_caf_prefix)
argname = "caf#";
if (strcmp(category(), "global") != 0) {
argname += category();
argname += ".";
}
argname += name();
if (short_name() != '\0') {
argname += ',';
argname += short_name();
}
return {std::move(argname), explanation(), ref_};
} }
config_reader_sink to_sink() override { /// Returns the full name for this config option as "<category>.<long name>".
return [=](size_t ln, config_value& x, optional<std::ostream&> errors) { std::string full_name() const;
auto res = get_if<T>(&x);
if (res) {
ref_ = *res;
return true;
}
type_name_visitor tnv;
report_type_error(ln, x, tnv(ref_), errors);
return false;
};
}
private: /// Checks whether `x` holds a legal value for this option.
T& ref_; error check(const config_value& x) const;
};
/// Stores `x` in this option unless it is stateless.
/// @pre `check(x) == none`.
void store(const config_value& x) const;
template <class T> private:
std::unique_ptr<config_option> std::string category_;
make_config_option(T& storage, const char* category, std::string long_name_;
const char* name, const char* explanation) { std::string short_names_;
auto ptr = new config_option_impl<T>(storage, category, name, explanation); std::string description_;
return std::unique_ptr<config_option>{ptr}; bool is_flag_;
} vtbl_type vtbl_;
mutable void* value_;
};
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "caf/config_option.hpp"
#include "caf/fwd.hpp"
#include "caf/make_config_option.hpp"
#include "caf/pec.hpp"
namespace caf {
/// A set of `config_option` objects that parses CLI arguments into a
/// `config_value::dictionary`.
class config_option_set {
public:
// -- member types -----------------------------------------------------------
/// An iterator over CLI arguments.
using argument_iterator = std::vector<std::string>::const_iterator;
/// The result of `parse` member functions.
using parse_result = std::pair<pec, argument_iterator>;
/// List of config options.
using option_vector = std::vector<config_option>;
/// Pointer to a config option.
using option_pointer = const config_option*;
/// An iterator over ::config_option unique pointers.
using iterator = option_vector::iterator;
/// Maps string keys to arbitrary (config) values.
using dictionary = std::map<std::string, config_value>;
/// Categorized settings.
using config_map = std::map<std::string, dictionary>;
// -- constructors, destructors, and assignment operators --------------------
config_option_set();
// -- properties -------------------------------------------------------------
/// Returns the first `config_option` that matches the CLI name.
/// @param name Config option name formatted as
/// `<prefix>#<category>.<long-name>`. Users can omit `<prefix>`
/// for options that have an empty prefix and `<category>`
/// if the category is "global".
option_pointer cli_long_name_lookup(const std::string& name) const;
/// Returns the first `config_option` that matches the CLI short option name.
option_pointer cli_short_name_lookup(char short_name) const;
/// Returns the first `config_option` that matches category and long name.
option_pointer qualified_name_lookup(const std::string& category,
const std::string& long_name) const;
/// Returns the first `config_option` that matches the qualified name.
/// @param name Config option name formatted as `<category>.<long-name>`.
option_pointer qualified_name_lookup(const std::string& name) const;
/// Returns the number of stored config options.
inline size_t size() const noexcept {
return opts_.size();
}
/// Returns an iterator to the first ::config_option object.
inline iterator begin() noexcept {
return opts_.begin();
}
/// Returns the past-the-end iterator.
inline iterator end() noexcept {
return opts_.end();
}
/// Adds a config option to the set.
template <class T>
config_option_set& add(const char* category, const char* name,
const char* description = "") {
opts_.emplace_back(make_config_option<T>(category, name, description));
return *this;
}
/// Adds a config option to the set that synchronizes its value with `ref`.
template <class T>
config_option_set& add(T& ref, const char* category, const char* name,
const char* description = "") {
opts_.emplace_back(make_config_option<T>(ref, category, name, description));
return *this;
}
// -- parsing ----------------------------------------------------------------
/// Parses a given range as CLI arguments into `config`.
parse_result parse(config_map& config, argument_iterator begin,
argument_iterator end) const;
/// Parses a given range as CLI arguments into `config`.
parse_result parse(config_map& config,
const std::vector<std::string>& args) const;
private:
// -- member variables -------------------------------------------------------
option_vector opts_;
};
} // namespace caf
...@@ -25,15 +25,11 @@ ...@@ -25,15 +25,11 @@
#include <vector> #include <vector>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/default_sum_type_access.hpp"
#include "caf/detail/bounds_checker.hpp" #include "caf/detail/bounds_checker.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/string_algorithms.hpp" #include "caf/string_algorithms.hpp"
#include "caf/sum_type.hpp"
#include "caf/sum_type_access.hpp"
#include "caf/timestamp.hpp" #include "caf/timestamp.hpp"
#include "caf/variant.hpp" #include "caf/variant.hpp"
...@@ -44,10 +40,6 @@ namespace caf { ...@@ -44,10 +40,6 @@ namespace caf {
/// contain lists of themselves. /// contain lists of themselves.
class config_value { class config_value {
public: public:
// -- friends ----------------------------------------------------------------
friend struct default_sum_type_access<config_value>;
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using integer = int64_t; using integer = int64_t;
...@@ -452,6 +444,67 @@ T get_or(const config_value::dictionary& xs, const std::string& name, ...@@ -452,6 +444,67 @@ T get_or(const config_value::dictionary& xs, const std::string& name,
return default_value; return default_value;
} }
/// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
optional<T> get_if(const std::map<std::string, config_value::dictionary>* xs,
const std::string& name) {
// Get the category.
auto pos = name.find('.');
if (pos == std::string::npos)
return none;
auto i = xs->find(name.substr(0, pos));
if (i == xs->end())
return none;
return get_if<T>(&i->second, name.substr(pos + 1));
}
/// Retrieves the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
T get(const std::map<std::string, config_value::dictionary>& xs,
const std::string& name) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
CAF_RAISE_ERROR("invalid type or name found");
}
/// Retrieves the value associated to `name` from `xs` or returns
/// `default_value`.
/// @relates config_value
template <class T>
T get_or(const std::map<std::string, config_value::dictionary>& xs,
const std::string& name, const T& default_value) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
return default_value;
}
/// Tries to retrieve the value associated to `name` from `cfg`.
/// @relates config_value
template <class T>
optional<T> get_if(const actor_system_config* cfg, const std::string& name) {
return get_if<T>(&content(*cfg), name);
}
/// Retrieves the value associated to `name` from `cfg`.
/// @relates config_value
template <class T>
T get(const actor_system_config& cfg, const std::string& name) {
return get<T>(content(cfg), name);
}
/// Retrieves the value associated to `name` from `cfg` or returns
/// `default_value`.
/// @relates config_value
template <class T>
T get_or(const actor_system_config& cfg, const std::string& name,
const T& default_value) {
return get_or(content(cfg), name, default_value);
}
/// @relates config_value /// @relates config_value
bool operator<(const config_value& x, const config_value& y); bool operator<(const config_value& x, const config_value& y);
......
...@@ -85,6 +85,49 @@ container_view<F, Container> make_container_view(Container& x) { ...@@ -85,6 +85,49 @@ container_view<F, Container> make_container_view(Container& x) {
return {x}; return {x};
} }
/// Like `std::find`, but takes a range instead of an iterator pair and returns
/// a pointer to the found object on success instead of returning an iterator.
template <class T>
typename T::value_type* ptr_find(T& xs, const typename T::value_type& x) {
for (auto& y : xs)
if (y == x)
return &y;
return nullptr;
}
/// Like `std::find`, but takes a range instead of an iterator pair and returns
/// a pointer to the found object on success instead of returning an iterator.
template <class T>
const typename T::value_type* ptr_find(const T& xs,
const typename T::value_type& x) {
for (auto& y : xs)
if (y == x)
return &y;
return nullptr;
}
/// Like `std::find_if`, but takes a range instead of an iterator pair and
/// returns a pointer to the found object on success instead of returning an
/// iterator.
template <class T, class Predicate>
typename T::value_type* ptr_find_if(T& xs, Predicate pred) {
for (auto& x : xs)
if (pred(x))
return &x;
return nullptr;
}
/// Like `std::find_if`, but takes a range instead of an iterator pair and
/// returns a pointer to the found object on success instead of returning an
/// iterator.
template <class T, class Predicate>
const typename T::value_type* ptr_find_if(const T& xs, Predicate pred) {
for (auto& x : xs)
if (pred(x))
return &x;
return nullptr;
}
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <map>
#include <string>
#include "caf/config_option_set.hpp"
#include "caf/config_value.hpp"
namespace caf {
namespace detail {
class ini_consumer;
class ini_list_consumer;
class ini_map_consumer;
class abstract_ini_consumer {
public:
// -- constructors, destructors, and assignment operators --------------------
explicit abstract_ini_consumer(abstract_ini_consumer* parent = nullptr);
virtual ~abstract_ini_consumer();
// -- properties -------------------------------------------------------------
virtual void value_impl(config_value&& x) = 0;
template <class T>
void value(T&& x) {
value_impl(config_value{std::forward<T>(x)});
}
inline abstract_ini_consumer* parent() {
return parent_;
}
ini_map_consumer begin_map();
ini_list_consumer begin_list();
protected:
// -- member variables -------------------------------------------------------
abstract_ini_consumer* parent_;
};
class ini_map_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
using map_type = config_value::dictionary;
using iterator = map_type::iterator;
// -- constructors, destructors, and assignment operators --------------------
ini_map_consumer(abstract_ini_consumer* ptr);
ini_map_consumer(ini_map_consumer&& other);
~ini_map_consumer() override;
// -- properties -------------------------------------------------------------
void end_map();
void key(std::string name);
void value_impl(config_value&& x) override;
private:
// -- member variables -------------------------------------------------------
map_type xs_;
iterator i_;
};
class ini_list_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
// -- constructors, destructors, and assignment operators --------------------
ini_list_consumer(abstract_ini_consumer* ptr);
ini_list_consumer(ini_list_consumer&& other);
// -- properties -------------------------------------------------------------
void end_list();
void value_impl(config_value&& x) override;
private:
// -- member variables -------------------------------------------------------
config_value::list xs_;
};
/// Consumes a single value from an INI parser.
class ini_value_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
// -- constructors, destructors, and assignment operators --------------------
explicit ini_value_consumer(abstract_ini_consumer* parent = nullptr);
// -- properties -------------------------------------------------------------
void value_impl(config_value&& x) override;
// -- member variables -------------------------------------------------------
config_value result;
};
/// Consumes a config category.
class ini_category_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
// -- constructors, destructors, and assignment operators --------------------
ini_category_consumer(ini_consumer* parent, std::string category);
ini_category_consumer(ini_category_consumer&&) = default;
// -- properties -------------------------------------------------------------
void end_map();
void key(std::string name);
void value_impl(config_value&& x) override;
private:
// -- properties -------------------------------------------------------------
ini_consumer* dparent();
// -- member variables -------------------------------------------------------
std::string category_;
config_value::dictionary xs_;
std::string current_key;
};
/// Consumes a series of dictionaries forming a application configuration.
class ini_consumer : public abstract_ini_consumer {
public:
// -- friends ----------------------------------------------------------------
friend class ini_category_consumer;
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
using config_map = std::map<std::string, config_value::dictionary>;
// -- constructors, destructors, and assignment operators --------------------
ini_consumer(config_option_set& options, config_map& cfg);
ini_consumer(ini_consumer&&) = default;
// -- properties -------------------------------------------------------------
ini_category_consumer begin_map();
void key(std::string name);
void value_impl(config_value&& x) override;
private:
// -- member variables -------------------------------------------------------
config_option_set& options_;
config_map& cfg_;
std::string current_key;
std::vector<error> warnings_;
};
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <type_traits>
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
/// Moves the value from `x` if it is not a pointer (e.g., `optional` or
/// `expected`), returns `*x` otherwise.
template <class T>
T& move_if_not_ptr(T* x) {
return *x;
}
/// Moves the value from `x` if it is not a pointer (e.g., `optional` or
/// `expected`), returns `*x` otherwise.
template <class T, class E = enable_if_t<!std::is_pointer<T>::value>>
T&& move_if_not_ptr(T& x) {
return std::move(*x);
}
} // namespace detail
} // namespace caf
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
struct parse_ini_t { struct parse_ini_t {
/// Denotes an optional error output stream /// Denotes an optional error output stream
using opt_err = optional<std::ostream&>; using opt_err = optional<std::ostream&>;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <cstring> #include <cstring>
#include "caf/detail/pp.hpp" #include "caf/detail/pp.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -55,8 +56,8 @@ extern const char octal_chars[9]; ...@@ -55,8 +56,8 @@ extern const char octal_chars[9];
} // namespace caf } // namespace caf
#define CAF_FSM_EVAL_MISMATCH_EC \ #define CAF_FSM_EVAL_MISMATCH_EC \
if (mismatch_ec == ec::unexpected_character) \ if (mismatch_ec == caf::pec::unexpected_character) \
ps.code = ch != '\n' ? mismatch_ec : ec::unexpected_newline; \ ps.code = ch != '\n' ? mismatch_ec : caf::pec::unexpected_newline; \
else \ else \
ps.code = mismatch_ec; \ ps.code = mismatch_ec; \
return; return;
...@@ -67,17 +68,17 @@ extern const char octal_chars[9]; ...@@ -67,17 +68,17 @@ extern const char octal_chars[9];
goto s_init; \ goto s_init; \
s_unexpected_eof: \ s_unexpected_eof: \
__attribute__((unused)); \ __attribute__((unused)); \
ps.code = ec::unexpected_eof; \ ps.code = caf::pec::unexpected_eof; \
return; \ return; \
{ \ { \
static constexpr ec mismatch_ec = ec::unexpected_character static constexpr auto mismatch_ec = caf::pec::unexpected_character
/// Defines a non-terminal state in the FSM. /// Defines a non-terminal state in the FSM.
#define state(name) \ #define state(name) \
CAF_FSM_EVAL_MISMATCH_EC \ CAF_FSM_EVAL_MISMATCH_EC \
} \ } \
{ \ { \
static constexpr ec mismatch_ec = ec::unexpected_character; \ static constexpr auto mismatch_ec = caf::pec::unexpected_character; \
s_##name : __attribute__((unused)); \ s_##name : __attribute__((unused)); \
if (ch == '\0') \ if (ch == '\0') \
goto s_unexpected_eof; \ goto s_unexpected_eof; \
...@@ -88,7 +89,7 @@ extern const char octal_chars[9]; ...@@ -88,7 +89,7 @@ extern const char octal_chars[9];
CAF_FSM_EVAL_MISMATCH_EC \ CAF_FSM_EVAL_MISMATCH_EC \
} \ } \
{ \ { \
static constexpr ec mismatch_ec = ec::trailing_character; \ static constexpr auto mismatch_ec = caf::pec::trailing_character; \
s_##name : __attribute__((unused)); \ s_##name : __attribute__((unused)); \
if (ch == '\0') \ if (ch == '\0') \
goto s_fin; \ goto s_fin; \
...@@ -99,7 +100,7 @@ extern const char octal_chars[9]; ...@@ -99,7 +100,7 @@ extern const char octal_chars[9];
CAF_FSM_EVAL_MISMATCH_EC \ CAF_FSM_EVAL_MISMATCH_EC \
} \ } \
s_fin: \ s_fin: \
ps.code = ec::success; \ ps.code = caf::pec::success; \
return; return;
#define CAF_TRANSITION_IMPL1(target) \ #define CAF_TRANSITION_IMPL1(target) \
...@@ -181,7 +182,7 @@ extern const char octal_chars[9]; ...@@ -181,7 +182,7 @@ extern const char octal_chars[9];
#define CAF_FSM_TRANSITION_IMPL2(fsm_call, target) \ #define CAF_FSM_TRANSITION_IMPL2(fsm_call, target) \
ps.next(); \ ps.next(); \
fsm_call; \ fsm_call; \
if (ps.code > ec::trailing_character) \ if (ps.code > caf::pec::trailing_character) \
return; \ return; \
ch = ps.current(); \ ch = ps.current(); \
goto s_##target; goto s_##target;
...@@ -218,7 +219,7 @@ extern const char octal_chars[9]; ...@@ -218,7 +219,7 @@ extern const char octal_chars[9];
#define CAF_FSM_EPSILON_IMPL2(fsm_call, target) \ #define CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
fsm_call; \ fsm_call; \
if (ps.code > ec::trailing_character) \ if (ps.code > caf::pec::trailing_character) \
return; \ return; \
ch = ps.current(); \ ch = ps.current(); \
goto s_##target; goto s_##target;
......
...@@ -23,13 +23,11 @@ ...@@ -23,13 +23,11 @@
#include <string> #include <string>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp" #include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -52,7 +50,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -52,7 +50,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
return true; return true;
}; };
auto g = caf::detail::make_scope_guard([&] { auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character) if (ps.code <= pec::trailing_character)
consumer.value(atom(buf)); consumer.value(atom(buf));
}); });
start(); start();
...@@ -62,7 +60,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -62,7 +60,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
} }
state(read_chars) { state(read_chars) {
transition(done, '\'') transition(done, '\'')
transition(read_chars, is_legal, append(ch), ec::too_many_characters) transition(read_chars, is_legal, append(ch), pec::too_many_characters)
} }
term_state(done) { term_state(done) {
transition(done, " \t") transition(done, " \t")
......
...@@ -21,12 +21,11 @@ ...@@ -21,12 +21,11 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp" #include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -37,7 +36,7 @@ template <class Iterator, class Sentinel, class Consumer> ...@@ -37,7 +36,7 @@ template <class Iterator, class Sentinel, class Consumer>
void read_bool(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_bool(state<Iterator, Sentinel>& ps, Consumer& consumer) {
bool res = false; bool res = false;
auto g = make_scope_guard([&] { auto g = make_scope_guard([&] {
if (ps.code <= ec::trailing_character) if (ps.code <= pec::trailing_character)
consumer.value(res); consumer.value(res);
}); });
start(); start();
......
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
#include <stack> #include <stack>
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/read_atom.hpp" #include "caf/detail/parser/read_atom.hpp"
#include "caf/detail/parser/read_bool.hpp" #include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_number_or_timespan.hpp" #include "caf/detail/parser/read_number_or_timespan.hpp"
#include "caf/detail/parser/read_string.hpp" #include "caf/detail/parser/read_string.hpp"
#include "caf/detail/scope_guard.hpp" #include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -52,7 +52,7 @@ namespace parser { ...@@ -52,7 +52,7 @@ namespace parser {
// //
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&) { void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&&) {
start(); start();
term_state(init) { term_state(init) {
transition(done, '\n') transition(done, '\n')
...@@ -65,7 +65,7 @@ void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&) { ...@@ -65,7 +65,7 @@ void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&) {
} }
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer); void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer);
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ini_list(state<Iterator, Sentinel>& ps, Consumer&& consumer) { void read_ini_list(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
...@@ -136,7 +136,7 @@ void read_ini_map(state<Iterator, Sentinel>& ps, Consumer&& consumer) { ...@@ -136,7 +136,7 @@ void read_ini_map(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
} }
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
start(); start();
state(init) { state(init) {
fsm_epsilon(read_string(ps, consumer), done, '"') fsm_epsilon(read_string(ps, consumer), done, '"')
...@@ -155,57 +155,26 @@ void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -155,57 +155,26 @@ void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer) {
/// Reads an INI formatted input. /// Reads an INI formatted input.
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_ini_section(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using std::swap; using std::swap;
std::string tmp; std::string tmp;
auto alnum_or_dash = [](char x) { auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_'; return isalnum(x) || x == '-' || x == '_';
}; };
bool in_section = false;
auto emit_key = [&] { auto emit_key = [&] {
std::string key; std::string key;
swap(tmp, key); swap(tmp, key);
consumer.key(std::move(key)); consumer.key(std::move(key));
}; };
auto begin_section = [&] {
if (in_section)
consumer.end_map();
else
in_section = true;
emit_key();
consumer.begin_map();
};
auto g = make_scope_guard([&] { auto g = make_scope_guard([&] {
if (ps.code <= ec::trailing_character && in_section) if (ps.code <= pec::trailing_character)
consumer.end_map(); consumer.end_map();
}); });
start(); start();
// Scanning for first section. // Dispatches to read sections, comments, or key/value pairs.
term_state(init) { term_state(init) {
transition(init, " \t\n") transition(init, " \t\n")
fsm_epsilon(read_ini_comment(ps, consumer), init, ';') fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(start_section, '[')
}
// Read the section key after reading an '['.
state(start_section) {
transition(start_section, " \t")
transition(read_section_name, alphabetic_chars, tmp = ch)
}
// Reads a section name such as "[foo]".
state(read_section_name) {
transition(read_section_name, alnum_or_dash, tmp += ch)
epsilon(close_section)
}
// Wait for the closing ']', preceded by any number of whitespaces.
state(close_section) {
transition(close_section, " \t")
transition(dispatch, ']', begin_section())
}
// Dispatches to read sections, comments, or key/value pairs.
term_state(dispatch) {
transition(dispatch, " \t\n")
transition(read_section_name, '[')
fsm_epsilon(read_ini_comment(ps, consumer), dispatch, ';')
transition(read_key_name, alphanumeric_chars, tmp = ch) transition(read_key_name, alphanumeric_chars, tmp = ch)
} }
// Reads a key of a "key=value" line. // Reads a key of a "key=value" line.
...@@ -226,8 +195,47 @@ void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -226,8 +195,47 @@ void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) {
// Waits for end-of-line after reading a value // Waits for end-of-line after reading a value
term_state(await_eol) { term_state(await_eol) {
transition(await_eol, " \t") transition(await_eol, " \t")
fsm_epsilon(read_ini_comment(ps, consumer), dispatch, ';') fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(dispatch, '\n') transition(init, '\n')
}
fin();
}
/// Reads an INI formatted input.
template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using std::swap;
std::string tmp;
auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_';
};
auto begin_section = [&]() -> decltype(consumer.begin_map()) {
std::string key;
swap(tmp, key);
consumer.key(std::move(key));
return consumer.begin_map();
};
start();
// Scanning for first section.
term_state(init) {
transition(init, " \t\n")
fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(start_section, '[')
}
// Read the section key after reading an '['.
state(start_section) {
transition(start_section, " \t")
transition(read_section_name, alphabetic_chars, tmp = ch)
}
// Reads a section name such as "[foo]".
state(read_section_name) {
transition(read_section_name, alnum_or_dash, tmp += ch)
epsilon(close_section)
}
// Wait for the closing ']', preceded by any number of whitespaces.
state(close_section) {
transition(close_section, " \t")
fsm_transition(read_ini_section(ps, begin_section()), init, ']')
} }
fin(); fin();
} }
......
...@@ -20,15 +20,14 @@ ...@@ -20,15 +20,14 @@
#include <cstdint> #include <cstdint>
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/add_ascii.hpp" #include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp" #include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp" #include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp" #include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -53,7 +52,7 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -53,7 +52,7 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
int64_t int_res = 0; int64_t int_res = 0;
// Computes the result on success. // Computes the result on success.
auto g = caf::detail::make_scope_guard([&] { auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character) { if (ps.code <= pec::trailing_character) {
if (result_type == integer) { if (result_type == integer) {
consumer.value(int_res); consumer.value(int_res);
return; return;
...@@ -63,11 +62,11 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -63,11 +62,11 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
exp += dec_exp; exp += dec_exp;
// 2) Check whether exponent is in valid range. // 2) Check whether exponent is in valid range.
if (exp < -max_double_exponent) { if (exp < -max_double_exponent) {
ps.code = ec::exponent_underflow; ps.code = pec::exponent_underflow;
return; return;
} }
if (exp > max_double_exponent) { if (exp > max_double_exponent) {
ps.code = ec::exponent_overflow; ps.code = pec::exponent_overflow;
return; return;
} }
// 3) Scale result. // 3) Scale result.
...@@ -140,13 +139,13 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -140,13 +139,13 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(pos_bin) epsilon(pos_bin)
} }
term_state(pos_bin) { term_state(pos_bin) {
transition(pos_bin, "01", add_ascii<2>(int_res, ch), ec::integer_overflow) transition(pos_bin, "01", add_ascii<2>(int_res, ch), pec::integer_overflow)
} }
state(start_neg_bin) { state(start_neg_bin) {
epsilon(neg_bin) epsilon(neg_bin)
} }
term_state(neg_bin) { term_state(neg_bin) {
transition(neg_bin, "01", sub_ascii<2>(int_res, ch), ec::integer_underflow) transition(neg_bin, "01", sub_ascii<2>(int_res, ch), pec::integer_underflow)
} }
// Octal integers. // Octal integers.
state(start_pos_oct) { state(start_pos_oct) {
...@@ -154,14 +153,14 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -154,14 +153,14 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
} }
term_state(pos_oct) { term_state(pos_oct) {
transition(pos_oct, octal_chars, add_ascii<8>(int_res, ch), transition(pos_oct, octal_chars, add_ascii<8>(int_res, ch),
ec::integer_overflow) pec::integer_overflow)
} }
state(start_neg_oct) { state(start_neg_oct) {
epsilon(neg_oct) epsilon(neg_oct)
} }
term_state(neg_oct) { term_state(neg_oct) {
transition(neg_oct, octal_chars, sub_ascii<8>(int_res, ch), transition(neg_oct, octal_chars, sub_ascii<8>(int_res, ch),
ec::integer_underflow) pec::integer_underflow)
} }
// Hexal integers. // Hexal integers.
state(start_pos_hex) { state(start_pos_hex) {
...@@ -169,32 +168,32 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -169,32 +168,32 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
} }
term_state(pos_hex) { term_state(pos_hex) {
transition(pos_hex, hexadecimal_chars, add_ascii<16>(int_res, ch), transition(pos_hex, hexadecimal_chars, add_ascii<16>(int_res, ch),
ec::integer_overflow) pec::integer_overflow)
} }
state(start_neg_hex) { state(start_neg_hex) {
epsilon(neg_hex) epsilon(neg_hex)
} }
term_state(neg_hex) { term_state(neg_hex) {
transition(neg_hex, hexadecimal_chars, sub_ascii<16>(int_res, ch), transition(neg_hex, hexadecimal_chars, sub_ascii<16>(int_res, ch),
ec::integer_underflow) pec::integer_underflow)
} }
// Reads the integer part of the mantissa or a positive decimal integer. // Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) { term_state(pos_dec) {
transition(pos_dec, decimal_chars, add_ascii<10>(int_res, ch), transition(pos_dec, decimal_chars, add_ascii<10>(int_res, ch),
ec::integer_overflow) pec::integer_overflow)
transition(has_e, "eE", ch_res(positive_double)) transition(has_e, "eE", ch_res(positive_double))
transition(trailing_dot, '.', ch_res(positive_double)) transition(trailing_dot, '.', ch_res(positive_double))
} }
// Reads the integer part of the mantissa or a negative decimal integer. // Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) { term_state(neg_dec) {
transition(neg_dec, decimal_chars, sub_ascii<10>(int_res, ch), transition(neg_dec, decimal_chars, sub_ascii<10>(int_res, ch),
ec::integer_underflow) pec::integer_underflow)
transition(has_e, "eE", ch_res(negative_double)) transition(has_e, "eE", ch_res(negative_double))
transition(trailing_dot, '.', ch_res(negative_double)) transition(trailing_dot, '.', ch_res(negative_double))
} }
// ".", "+.", etc. aren't valid numbers, so this state isn't terminal. // ".", "+.", etc. aren't valid numbers, so this state isn't terminal.
state(leading_dot) { state(leading_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), ec::exponent_underflow) transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
} }
// "1." is a valid number, so a trailing dot is a terminal state. // "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) { term_state(trailing_dot) {
...@@ -202,7 +201,7 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -202,7 +201,7 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
} }
// Read the decimal part of a mantissa. // Read the decimal part of a mantissa.
term_state(after_dot) { term_state(after_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), ec::exponent_underflow) transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
transition(has_e, "eE") transition(has_e, "eE")
} }
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not // "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
...@@ -211,25 +210,25 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -211,25 +210,25 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
transition(has_plus_after_e, '+') transition(has_plus_after_e, '+')
transition(has_minus_after_e, '-') transition(has_minus_after_e, '-')
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch), transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow) pec::exponent_overflow)
} }
state(has_plus_after_e) { state(has_plus_after_e) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch), transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow) pec::exponent_overflow)
} }
state(has_minus_after_e) { state(has_minus_after_e) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch), transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
ec::exponent_underflow) pec::exponent_underflow)
} }
// Read a positive exponent. // Read a positive exponent.
term_state(pos_exp) { term_state(pos_exp) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch), transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow) pec::exponent_overflow)
} }
// Read a negative exponent. // Read a negative exponent.
term_state(neg_exp) { term_state(neg_exp) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch), transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
ec::exponent_underflow) pec::exponent_underflow)
} }
fin(); fin();
} }
......
...@@ -22,18 +22,16 @@ ...@@ -22,18 +22,16 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp" #include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/read_number.hpp" #include "caf/detail/parser/read_number.hpp"
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -60,7 +58,7 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps, ...@@ -60,7 +58,7 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
auto has_dbl = [&] { return holds_alternative<double>(ic.interim); }; auto has_dbl = [&] { return holds_alternative<double>(ic.interim); };
auto get_int = [&] { return get<int64_t>(ic.interim); }; auto get_int = [&] { return get<int64_t>(ic.interim); };
auto g = make_scope_guard([&] { auto g = make_scope_guard([&] {
if (ps.code <= ec::trailing_character) { if (ps.code <= pec::trailing_character) {
if (res != none) { if (res != none) {
consumer.value(*res); consumer.value(*res);
} else if (!holds_alternative<none_t>(ic.interim)) { } else if (!holds_alternative<none_t>(ic.interim)) {
...@@ -80,7 +78,7 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps, ...@@ -80,7 +78,7 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
epsilon_if(has_dbl(), has_double) epsilon_if(has_dbl(), has_double)
} }
term_state(has_double) { term_state(has_double) {
error_transition(ec::fractional_timespan, "unms") error_transition(pec::fractional_timespan, "unms")
} }
term_state(has_integer) { term_state(has_integer) {
transition(have_u, 'u') transition(have_u, 'u')
......
...@@ -23,10 +23,10 @@ ...@@ -23,10 +23,10 @@
#include "caf/detail/scope_guard.hpp" #include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp" #include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -38,7 +38,7 @@ template <class Iterator, class Sentinel, class Consumer> ...@@ -38,7 +38,7 @@ template <class Iterator, class Sentinel, class Consumer>
void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
std::string res; std::string res;
auto g = caf::detail::make_scope_guard([&] { auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character) if (ps.code <= pec::trailing_character)
consumer.value(std::move(res)); consumer.value(std::move(res));
}); });
start(); start();
...@@ -49,7 +49,7 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -49,7 +49,7 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
state(read_chars) { state(read_chars) {
transition(escape, '\\') transition(escape, '\\')
transition(done, '"') transition(done, '"')
error_transition(ec::unexpected_newline, '\n') error_transition(pec::unexpected_newline, '\n')
transition(read_chars, any_char, res += ch) transition(read_chars, any_char, res += ch)
} }
state(escape) { state(escape) {
...@@ -58,7 +58,7 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -58,7 +58,7 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
transition(read_chars, 't', res += '\t') transition(read_chars, 't', res += '\t')
transition(read_chars, '\\', res += '\\') transition(read_chars, '\\', res += '\\')
transition(read_chars, '"', res += '"') transition(read_chars, '"', res += '"')
error_transition(ec::illegal_escape_sequence) error_transition(pec::illegal_escape_sequence)
} }
term_state(done) { term_state(done) {
transition(done, " \t") transition(done, " \t")
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <cstdint> #include <cstdint>
#include "caf/detail/parser/ec.hpp" #include "caf/pec.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -30,7 +30,7 @@ template <class Iterator, class Sentinel = Iterator> ...@@ -30,7 +30,7 @@ template <class Iterator, class Sentinel = Iterator>
struct state { struct state {
Iterator i; Iterator i;
Sentinel e; Sentinel e;
ec code; pec code;
int32_t line = 1; int32_t line = 1;
int32_t column = 1; int32_t column = 1;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <map>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
...@@ -138,6 +139,12 @@ struct prohibit_top_level_spawn_marker; ...@@ -138,6 +139,12 @@ struct prohibit_top_level_spawn_marker;
enum class stream_priority; enum class stream_priority;
enum class atom_value : uint64_t; enum class atom_value : uint64_t;
// -- functions ----------------------------------------------------------------
/// @relates actor_system_config
const std::map<std::string, std::map<std::string, config_value>>&
content(const actor_system_config&);
// -- aliases ------------------------------------------------------------------ // -- aliases ------------------------------------------------------------------
using actor_id = uint64_t; using actor_id = uint64_t;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/pec.hpp"
namespace caf {
/// Creates a config option that synchronizes with `storage`.
template <class T>
config_option make_config_option(const char* category, const char* name,
const char* description) {
config_option::vtbl_type vtbl{
[](const config_value& x) -> error {
if (holds_alternative<T>(x))
return none;
return make_error(pec::type_mismatch);
},
nullptr
};
return {category, name, description, std::is_same<T, bool>::value, vtbl};
}
/// Creates a config option that synchronizes with `storage`.
template <class T>
config_option make_config_option(T& storage, const char* category,
const char* name, const char* description) {
config_option::vtbl_type vtbl{
[](const config_value& x) -> error {
if (holds_alternative<T>(x))
return none;
return make_error(pec::type_mismatch);
},
[](void* ptr, const config_value& x) {
*static_cast<T*>(ptr) = get<T>(x);
}
};
return {category, name, description, std::is_same<T, bool>::value,
vtbl, &storage};
}
} // namespace caf
...@@ -23,14 +23,14 @@ ...@@ -23,14 +23,14 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
namespace caf { namespace caf {
namespace detail {
namespace parser {
enum class ec : uint8_t { /// PEC stands for "Parser Error Code". This enum contains error codes used by
/// various CAF parsers.
enum class pec : uint8_t {
/// Not-an-error. /// Not-an-error.
success, success = 0,
/// Parser succeeded but found trailing character(s). /// Parser succeeded but found trailing character(s).
trailing_character, trailing_character = 1,
/// Parser stopped after reaching the end while still expecting input. /// Parser stopped after reaching the end while still expecting input.
unexpected_eof, unexpected_eof,
/// Parser stopped after reading an unexpected character. /// Parser stopped after reading an unexpected character.
...@@ -38,7 +38,7 @@ enum class ec : uint8_t { ...@@ -38,7 +38,7 @@ enum class ec : uint8_t {
/// Parsed integer exceeds the number of available bits of a `timespan`. /// Parsed integer exceeds the number of available bits of a `timespan`.
timespan_overflow, timespan_overflow,
/// Tried constructing a `timespan` with from a floating point number. /// Tried constructing a `timespan` with from a floating point number.
fractional_timespan, fractional_timespan = 5,
/// Too many characters for an atom. /// Too many characters for an atom.
too_many_characters, too_many_characters,
/// Unrecognized character after escaping `\`. /// Unrecognized character after escaping `\`.
...@@ -48,19 +48,27 @@ enum class ec : uint8_t { ...@@ -48,19 +48,27 @@ enum class ec : uint8_t {
/// Parsed positive integer exceeds the number of available bits. /// Parsed positive integer exceeds the number of available bits.
integer_overflow, integer_overflow,
/// Parsed negative integer exceeds the number of available bits. /// Parsed negative integer exceeds the number of available bits.
integer_underflow, integer_underflow = 10,
/// Exponent of parsed double is less than the minimum supported exponent. /// Exponent of parsed double is less than the minimum supported exponent.
exponent_underflow, exponent_underflow,
/// Exponent of parsed double is greater than the maximum supported exponent. /// Exponent of parsed double is greater than the maximum supported exponent.
exponent_overflow, exponent_overflow,
/// Parsed type does not match the expected type. /// Parsed type does not match the expected type.
type_mismatch, type_mismatch,
/// Stopped at an unrecognized option name.
not_an_option,
/// Stopped at an unparseable argument.
illegal_argument = 15,
/// Stopped because an argument was omitted.
missing_argument,
/// Stopped because the key of a category was taken.
illegal_category,
}; };
error make_error(ec code); error make_error(pec code);
const char* to_string(ec x); error make_error(pec code, size_t line, size_t column);
const char* to_string(pec x);
} // namespace parser
} // namespace detail
} // namespace caf } // namespace caf
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
namespace caf { namespace caf {
/// SEC stands for "System Error Code". This enum contains /// SEC stands for "System Error Code". This enum contains error codes for
/// error codes used internally by CAF. /// ::actor_system and its modules.
enum class sec : uint8_t { enum class sec : uint8_t {
/// No error. /// No error.
none = 0, none = 0,
......
...@@ -23,70 +23,18 @@ ...@@ -23,70 +23,18 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include "caf/message_builder.hpp"
#include "caf/detail/gcd.hpp" #include "caf/detail/gcd.hpp"
#include "caf/detail/parse_ini.hpp" #include "caf/detail/ini_consumer.hpp"
#include "caf/detail/parser/read_ini.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/message_builder.hpp"
namespace caf { namespace caf {
namespace { actor_system_config::opt_group::opt_group(config_option_set& xs,
using option_vector = actor_system_config::option_vector;
const char actor_conf_prefix[] = "actor:";
constexpr size_t actor_conf_prefix_size = 6;
class actor_system_config_reader {
public:
using sink = std::function<void (size_t, config_value&,
optional<std::ostream&>)>;
using named_actor_sink = std::function<void (size_t, const std::string&,
config_value&)>;
actor_system_config_reader(option_vector& xs, option_vector& ys,
named_actor_sink na_sink)
: named_actor_sink_(std::move(na_sink)){
add_opts(xs);
add_opts(ys);
}
void add_opts(option_vector& xs) {
for (auto& x : xs)
sinks_.emplace(x->full_name(), x->to_sink());
}
bool operator()(size_t ln, const std::string& name, config_value& cv,
optional<std::ostream&> out) {
auto i = sinks_.find(name);
if (i != sinks_.end()) {
(i->second)(ln, cv, none);
return true;
}
// check whether this is an individual actor config
if (name.compare(0, actor_conf_prefix_size, actor_conf_prefix) == 0) {
auto substr = name.substr(actor_conf_prefix_size);
named_actor_sink_(ln, substr, cv);
return true;
}
if (out)
*out << "error in line " << ln
<< R"(: unrecognized parameter name ")" << name << R"(")"
<< std::endl;
return false;
}
private:
std::map<std::string, sink> sinks_;
named_actor_sink named_actor_sink_;
};
} // namespace <anonymous>
actor_system_config::opt_group::opt_group(option_vector& xs,
const char* category) const char* category)
: xs_(xs), : xs_(xs),
cat_(category) { category_(category) {
// nop // nop
} }
...@@ -279,21 +227,35 @@ actor_system_config::make_help_text(const std::vector<message::cli_arg>& xs) { ...@@ -279,21 +227,35 @@ actor_system_config::make_help_text(const std::vector<message::cli_arg>& xs) {
actor_system_config& actor_system_config::parse(int argc, char** argv, actor_system_config& actor_system_config::parse(int argc, char** argv,
const char* ini_file_cstr) { const char* ini_file_cstr) {
message args; if (argc < 2)
if (argc > 1) return *this;
args = message_builder(argv + 1, argv + argc).move_to_message(); string_list args{argv + 1, argv + argc};
return parse(args, ini_file_cstr); return parse(std::move(args), ini_file_cstr);
} }
actor_system_config& actor_system_config::parse(int argc, char** argv, actor_system_config& actor_system_config::parse(int argc, char** argv,
std::istream& ini) { std::istream& ini) {
message args; if (argc < 2)
if (argc > 1) return *this;
args = message_builder(argv + 1, argv + argc).move_to_message(); string_list args{argv + 1, argv + argc};
return parse(args, ini); return parse(std::move(args), ini);
}
actor_system_config& actor_system_config::parse(string_list args,
std::istream& ini) {
// (2) content of the INI file overrides hard-coded defaults
if (ini.good()) {
detail::ini_consumer consumer{options_, content};
detail::parser::state<std::istream_iterator<char>> res;
res.i = std::istream_iterator<char>{ini};
detail::parser::read_ini(res, consumer);
}
// (3) CLI options override the content of the INI file
options_.parse(content, args);
return *this;
} }
actor_system_config& actor_system_config::parse(message& args, actor_system_config& actor_system_config::parse(string_list args,
const char* ini_file_cstr) { const char* ini_file_cstr) {
// Override default config file name if set by user. // Override default config file name if set by user.
if (ini_file_cstr != nullptr) if (ini_file_cstr != nullptr)
...@@ -301,111 +263,25 @@ actor_system_config& actor_system_config::parse(message& args, ...@@ -301,111 +263,25 @@ actor_system_config& actor_system_config::parse(message& args,
// CLI arguments always win. // CLI arguments always win.
extract_config_file_path(args); extract_config_file_path(args);
std::ifstream ini{config_file_path}; std::ifstream ini{config_file_path};
return parse(args, ini); return parse(std::move(args), ini);
} }
actor_system_config& actor_system_config::parse(message& args, actor_system_config& actor_system_config::parse(message& msg,
const char* ini_file_cstr) {
string_list args;
for (size_t i = 0; i < msg.size(); ++i)
if (msg.match_element<std::string>(i))
args.emplace_back(msg.get_as<std::string>(i));
return parse(std::move(args), ini_file_cstr);
}
actor_system_config& actor_system_config::parse(message& msg,
std::istream& ini) { std::istream& ini) {
// (2) content of the INI file overrides hard-coded defaults string_list args;
if (ini.good()) { for (size_t i = 0; i < msg.size(); ++i)
using conf_sink = std::function<void (size_t, config_value&, if (msg.match_element<std::string>(i))
optional<std::ostream&>)>; args.emplace_back(msg.get_as<std::string>(i));
using conf_sinks = std::unordered_map<std::string, conf_sink>; return parse(std::move(args), ini);
using conf_mapping = std::pair<option_vector, conf_sinks>;
hash_map<std::string, conf_mapping> ovs;
auto nac_sink = [&](size_t ln, const std::string& nm, config_value& cv) {
std::string actor_name{nm.begin(), std::find(nm.begin(), nm.end(), '.')};
auto ac = named_actor_configs.find(actor_name);
if (ac == named_actor_configs.end())
ac = named_actor_configs.emplace(actor_name,
named_actor_config{}).first;
auto& ov = ovs[actor_name];
if (ov.first.empty()) {
opt_group(ov.first, ac->first.c_str())
.add(ac->second.strategy, "strategy", "")
.add(ac->second.low_watermark, "low-watermark", "")
.add(ac->second.max_pending, "max-pending", "");
for (auto& opt : ov.first)
ov.second.emplace(opt->full_name(), opt->to_sink());
}
auto i = ov.second.find(nm);
if (i != ov.second.end())
i->second(ln, cv, none);
else
std::cerr << "error in line " << ln
<< R"(: unrecognized parameter name ")" << nm << R"(")"
<< std::endl;
};
actor_system_config_reader consumer{options_, custom_options_, nac_sink};
detail::parse_ini(ini, consumer, std::cerr);
}
// (3) CLI options override the content of the INI file
std::string dummy; // caf#config-file either ignored or already open
std::vector<message::cli_arg> cargs;
for (auto& x : options_)
cargs.emplace_back(x->to_cli_arg(true));
cargs.emplace_back("caf#dump-config", "print config in INI format to stdout");
//cargs.emplace_back("caf#help", "print this text");
cargs.emplace_back("caf#config-file", "parse INI file", dummy);
cargs.emplace_back("caf#slave-mode", "run in slave mode");
cargs.emplace_back("caf#slave-name", "set name for this slave", slave_name);
cargs.emplace_back("caf#bootstrap-node", "set bootstrapping", bootstrap_node);
for (auto& x : custom_options_)
cargs.emplace_back(x->to_cli_arg(false));
using std::placeholders::_1;
auto res = args.extract_opts(std::move(cargs),
std::bind(&actor_system_config::make_help_text,
this, _1));
using std::cerr;
using std::cout;
using std::endl;
args_remainder = std::move(res.remainder);
if (!res.error.empty()) {
cli_helptext_printed = true;
std::cerr << res.error << endl;
return *this;
}
if (res.opts.count("help") != 0u) {
cli_helptext_printed = true;
cout << res.helptext << endl;
return *this;
}
if (res.opts.count("caf#slave-mode") != 0u) {
slave_mode = true;
if (slave_name.empty())
std::cerr << "running in slave mode but no name was configured" << endl;
if (bootstrap_node.empty())
std::cerr << "running in slave mode without bootstrap node" << endl;
}
// Verify settings.
auto verify_atom_opt = [](std::initializer_list<atom_value> xs, atom_value& x,
const char* xname) {
if (std::find(xs.begin(), xs.end(), x) == xs.end()) {
cerr << "[WARNING] invalid value for " << xname
<< " defined, falling back to "
<< deep_to_string(*xs.begin()) << endl;
x = *xs.begin();
}
};
verify_atom_opt({atom("default"),
# ifdef CAF_USE_ASIO
atom("asio")
# endif
}, middleman_network_backend, "middleman.network-backend");
verify_atom_opt({atom("stealing"), atom("sharing"), atom("testing")},
scheduler_policy, "scheduler.policy ");
if (res.opts.count("caf#dump-config") != 0u) {
cli_helptext_printed = true;
std::string category;
for_each_option([&](const config_option& x) {
if (category != x.category()) {
category = x.category();
cout << "[" << category << "]" << endl;
}
cout << x.name() << "=" << x.to_string() << endl;
});
}
return *this;
} }
actor_system_config& actor_system_config&
...@@ -420,15 +296,12 @@ actor_system_config::add_error_category(atom_value x, error_renderer y) { ...@@ -420,15 +296,12 @@ actor_system_config::add_error_category(atom_value x, error_renderer y) {
return *this; return *this;
} }
actor_system_config& actor_system_config::set_impl(const char* cn, actor_system_config& actor_system_config::set_impl(const char* name,
config_value cv) { config_value value) {
auto e = options_.end(); auto opt = options_.qualified_name_lookup(name);
auto i = std::find_if(options_.begin(), e, [cn](const option_ptr& ptr) { if (opt != nullptr && opt->check(value) == none) {
return ptr->full_name() == cn; opt->store(value);
}); content[opt->category()][name] = std::move(value);
if (i != e) {
auto f = (*i)->to_sink();
f(0, cv, none);
} }
return *this; return *this;
} }
...@@ -452,11 +325,46 @@ std::string actor_system_config::render_exit_reason(uint8_t x, atom_value, ...@@ -452,11 +325,46 @@ std::string actor_system_config::render_exit_reason(uint8_t x, atom_value,
meta::omittable_if_empty(), xs); meta::omittable_if_empty(), xs);
} }
void actor_system_config::extract_config_file_path(message& args) { void actor_system_config::extract_config_file_path(string_list& args) {
auto res = args.extract_opts({ static constexpr const char needle[] = "--caf#config-file=";
{"caf#config-file", "", config_file_path} auto last = args.end();
auto i = std::find_if(args.begin(), last, [](const std::string& arg) {
return arg.compare(0, sizeof(needle) - 1, needle) == 0;
}); });
args = res.remainder; if (i == last)
return;
auto arg_begin = i->begin() + sizeof(needle);
auto arg_end = i->end();
if (arg_begin == arg_end) {
// Missing value.
// TODO: print warning?
return;
}
if (*arg_begin == '"') {
detail::parser::state<std::string::const_iterator> res;
res.i = arg_begin;
res.e = arg_end;
struct consumer {
std::string result;
void value(std::string&& x) {
result = std::move(x);
}
};
consumer f;
detail::parser::read_string(res, f);
if (res.code == pec::success)
config_file_path = std::move(f.result);
// TODO: else print warning?
} else {
// We support unescaped strings for convenience on the CLI.
config_file_path = std::string{arg_begin, arg_end};
}
args.erase(i);
}
const std::map<std::string, std::map<std::string, config_value>>&
content(const actor_system_config& cfg) {
return cfg.content;
} }
} // namespace caf } // namespace caf
...@@ -20,6 +20,28 @@ ...@@ -20,6 +20,28 @@
#include <iostream> #include <iostream>
#include "caf/config.hpp"
#include "caf/error.hpp"
using std::move;
using std::string;
namespace {
string get_long_name(const char* name) {
auto i= strchr(name, ',');
return i != nullptr ? string{name, i} : string{name};
}
string get_short_names(const char* name) {
auto substr = strchr(name, ',');
if (substr != nullptr)
return ++substr;
return string{};
}
} // namespace <anonymous>
namespace caf { namespace caf {
const char* type_name_visitor_tbl[] { const char* type_name_visitor_tbl[] {
...@@ -39,49 +61,36 @@ const char* type_name_visitor_tbl[] { ...@@ -39,49 +61,36 @@ const char* type_name_visitor_tbl[] {
"a duration" "a duration"
}; };
config_option::config_option(const char* cat, const char* nm, const char* expl) config_option::config_option(string category, const char* name,
: category_(cat), string description, bool is_flag,
name_(nm), const vtbl_type& vtbl, void* value)
explanation_(expl), : category_(move(category)),
short_name_('\0') { long_name_(get_long_name(name)),
auto last = name_.end(); short_names_(get_short_names(name)),
auto comma = std::find(name_.begin(), last, ','); description_(move(description)),
if (comma != last) { is_flag_(is_flag),
auto i = comma; vtbl_(vtbl),
++i; value_(value) {
if (i != last) // nop
short_name_ = *i;
name_.erase(comma, last);
}
} }
config_option::~config_option() { string config_option::full_name() const {
// nop std::string result = category();
result += '.';
result += long_name_;
return result;
} }
std::string config_option::full_name() const { error config_option::check(const config_value& x) const {
std::string res = category(); CAF_ASSERT(vtbl_.check != nullptr);
res += '.'; return vtbl_.check(x);
auto name_begin = name();
const char* name_end = strchr(name(), ',');
if (name_end != nullptr)
res.insert(res.end(), name_begin, name_end);
else
res += name();
return res;
} }
void config_option::report_type_error(size_t ln, config_value& x, void config_option::store(const config_value& x) const {
const char* expected, if (value_ != nullptr) {
optional<std::ostream&> out) { CAF_ASSERT(vtbl_.store != nullptr);
if (!out) vtbl_.store(value_, x);
return; }
/*
type_name_visitor tnv;
*out << "error in line " << ln << ": expected "
<< expected << " found "
<< visit(tnv, x) << '\n';
*/
} }
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config_option_set.hpp"
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/algorithms.hpp"
#include "caf/expected.hpp"
using std::string;
namespace caf {
config_option_set::config_option_set() {
// nop
}
auto config_option_set::parse(config_map& config, argument_iterator first,
argument_iterator last) const
-> std::pair<pec, argument_iterator> {
// Sanity check.
if (first == last)
return {pec::success, last};
// Parses an argument.
using iter = string::const_iterator;
auto consume = [&](const config_option& opt, iter arg_begin, iter arg_end) {
// Extract option name and category.
auto opt_name = opt.long_name();
string opt_ctg = opt.category();
// Try inserting a new submap into the config or fill existing one.
auto& submap = config[opt_ctg];
// Flags only consume the current element.
if (opt.is_flag()) {
if (arg_begin != arg_end)
return pec::illegal_argument;
config_value cfg_true{true};
opt.store(cfg_true);
submap[opt_name] = cfg_true;
} else {
if (arg_begin == arg_end)
return pec::missing_argument;
auto val = config_value::parse(arg_begin, arg_end);
if (!val)
return pec::illegal_argument;
if (opt.check(*val) != none)
return pec::type_mismatch;
opt.store(*val);
submap[opt_name] = std::move(*val);
}
return pec::success;
};
// We loop over the first N-1 values, because we always consider two
// arguments at once.
for (auto i = first; i != last;) {
if (i->size() < 2)
return {pec::not_an_option, i};
if (*i== "--")
return {pec::success, std::next(first)};
if (i->compare(0, 2, "--") == 0) {
// Long options use the syntax "--<name>=<value>" and consume only a
// single argument.
auto assign_op = i->find('=');//std::find(i->begin(), i->end(), '=');
auto name = i->substr(2, assign_op - 2);
auto opt = cli_long_name_lookup(name);
if (opt == nullptr)
return {pec::not_an_option, i};
auto code = consume(*opt, i->begin() + assign_op + 1, i->end());
if (code != pec::success)
return {code, i};
++i;
} else if (i->front() == '-') {
// Short options have three possibilities.
auto opt = cli_short_name_lookup((*i)[1]);
if (opt == nullptr)
return {pec::not_an_option, i};
if (opt->is_flag()) {
// 1) "-f" for flags, consumes one argument
auto code = consume(*opt, i->begin() + 2, i->end());
if (code != pec::success)
return {code, i};
++i;
} else {
if (i->size() == 2) {
// 2) "-k <value>", consumes both arguments
auto j = std::next(i);
if (j == last) {
return {pec::missing_argument, j};
}
auto code = consume(*opt, j->begin(), j->end());
if (code != pec::success)
return {code, i};
std::advance(i, 2);
} else {
// 3) "-k<value>" (no space), consumes one argument
auto code = consume(*opt, i->begin() + 2, i->end());
if (code != pec::success)
return {code, i};
++i;
}
}
} else {
// No leading '-' found on current position.
return {pec::not_an_option, i};
}
}
return {pec::success, last};
}
config_option_set::parse_result
config_option_set::parse(config_map& config,
const std::vector<string>& args) const {
return parse(config, args.begin(), args.end());
}
config_option_set::option_pointer
config_option_set::cli_long_name_lookup(const string& name) const {
// We accept "caf#" prefixes for backward compatibility, but ignore them.
size_t offset = name.compare(0, 4, "caf#") != 0 ? 0u : 4u;
// Extract category and long name.
string category;
string long_name;
auto sep = name.find('.', offset);
if (sep == string::npos) {
category = "global";
if (offset == 0)
long_name = name;
else
long_name = name.substr(offset);
} else {
category = name.substr(offset, sep);
long_name = name.substr(sep + 1);
}
// Scan all options for a match.
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.category() == category && opt.long_name() == long_name;
});
}
config_option_set::option_pointer
config_option_set::cli_short_name_lookup(char short_name) const {
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.short_names().find(short_name) != string::npos;
});
}
config_option_set::option_pointer
config_option_set::qualified_name_lookup(const std::string& category,
const std::string& long_name) const {
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.category() == category && opt.long_name() == long_name;
});
}
config_option_set::option_pointer
config_option_set::qualified_name_lookup(const std::string& name) const {
auto sep = name.find('.');
if (sep == string::npos)
return nullptr;
return qualified_name_lookup(name.substr(0, sep), name.substr(sep + 1));
}
} // namespace caf
...@@ -19,10 +19,11 @@ ...@@ -19,10 +19,11 @@
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/detail/parser/ec.hpp" #include "caf/detail/ini_consumer.hpp"
#include "caf/detail/parser/read_ini.hpp" #include "caf/detail/parser/read_ini.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
#include "caf/pec.hpp"
namespace caf { namespace caf {
...@@ -49,133 +50,24 @@ config_value::~config_value() { ...@@ -49,133 +50,24 @@ config_value::~config_value() {
// -- parsing ------------------------------------------------------------------ // -- parsing ------------------------------------------------------------------
namespace {
struct abstract_consumer {
virtual ~abstract_consumer() {
// nop
}
template <class T>
void value(T&& x) {
value_impl(config_value{std::forward<T>(x)});
}
virtual void value_impl(config_value&& x) = 0;
};
struct list_consumer;
struct map_consumer : abstract_consumer {
using map_type = config_value::dictionary;
using iterator = map_type::iterator;
map_consumer begin_map() {
return {this};
}
void end_map() {
parent->value_impl(config_value{std::move(xs)});
}
list_consumer begin_list();
void key(std::string name) {
i = xs.emplace_hint(xs.end(), std::make_pair(std::move(name), config_value{}));
}
void value_impl(config_value&& x) override {
i->second = std::move(x);
}
map_consumer(abstract_consumer* ptr) : i(xs.end()), parent(ptr) {
// nop
}
map_consumer(map_consumer&& other)
: xs(std::move(other.xs)),
i(xs.end()),
parent(other.parent) {
// nop
}
map_type xs;
iterator i;
abstract_consumer* parent;
};
struct list_consumer : abstract_consumer {
void end_list() {
parent->value_impl(config_value{std::move(xs)});
}
map_consumer begin_map() {
return {this};
}
list_consumer begin_list() {
return {this};
}
void value_impl(config_value&& x) override {
xs.emplace_back(std::move(x));
}
list_consumer(abstract_consumer* ptr) : parent(ptr) {
// nop
}
list_consumer(list_consumer&& other)
: xs(std::move(other.xs)),
parent(other.parent) {
// nop
}
config_value::list xs;
abstract_consumer* parent;
};
list_consumer map_consumer::begin_list() {
return {this};
}
struct consumer : abstract_consumer {
config_value result;
map_consumer begin_map() {
return {this};
}
list_consumer begin_list() {
return {this};
}
void value_impl(config_value&& x) override {
result = std::move(x);
}
};
} // namespace <anonymous>
expected<config_value> config_value::parse(std::string::const_iterator first, expected<config_value> config_value::parse(std::string::const_iterator first,
std::string::const_iterator last) { std::string::const_iterator last) {
using namespace detail; using namespace detail;
auto i = first; auto i = first;
// Sanity check. // Sanity check.
if (i == last) if (i == last)
return make_error(parser::ec::unexpected_eof); return make_error(pec::unexpected_eof);
// Skip to beginning of the argument. // Skip to beginning of the argument.
while (isspace(*i)) while (isspace(*i))
if (++i == last) if (++i == last)
return make_error(parser::ec::unexpected_eof); return make_error(pec::unexpected_eof);
// Dispatch to parser. // Dispatch to parser.
parser::state<std::string::const_iterator> res; parser::state<std::string::const_iterator> res;
consumer f; detail::ini_value_consumer f;
res.i = i; res.i = i;
res.e = last; res.e = last;
parser::read_ini_value(res, f); parser::read_ini_value(res, f);
if (res.code == detail::parser::ec::success) if (res.code == pec::success)
return std::move(f.result); return std::move(f.result);
// Assume an unescaped string unless the first character clearly indicates // Assume an unescaped string unless the first character clearly indicates
// otherwise. // otherwise.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/ini_consumer.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
// -- abstract_ini_consumer ----------------------------------------------------
abstract_ini_consumer::abstract_ini_consumer(abstract_ini_consumer* parent)
: parent_(parent) {
// nop
}
abstract_ini_consumer::~abstract_ini_consumer() {
// nop
}
ini_map_consumer abstract_ini_consumer::begin_map() {
return {this};
}
ini_list_consumer abstract_ini_consumer::begin_list() {
return {this};
}
// -- map_consumer -------------------------------------------------------------
ini_map_consumer::ini_map_consumer(abstract_ini_consumer* parent)
: super(parent),
i_(xs_.end()) {
CAF_ASSERT(parent != nullptr);
}
ini_map_consumer::ini_map_consumer(ini_map_consumer&& other)
: super(other.parent()) {
}
ini_map_consumer::~ini_map_consumer() {
// nop
}
void ini_map_consumer::end_map() {
parent_->value_impl(config_value{std::move(xs_)});
}
void ini_map_consumer::key(std::string name) {
i_ = xs_.emplace(std::move(name), config_value{}).first;
}
void ini_map_consumer::value_impl(config_value&& x) {
CAF_ASSERT(i_ != xs_.end());
i_->second = std::move(x);
}
// -- ini_list_consumer --------------------------------------------------------
ini_list_consumer::ini_list_consumer(abstract_ini_consumer* parent)
: super(parent) {
CAF_ASSERT(parent != nullptr);
}
ini_list_consumer::ini_list_consumer(ini_list_consumer&& other)
: super(other.parent()),
xs_(std::move(other.xs_)) {
// nop
}
void ini_list_consumer::end_list() {
parent_->value_impl(config_value{std::move(xs_)});
}
void ini_list_consumer::value_impl(config_value&& x) {
xs_.emplace_back(std::move(x));
}
// -- ini_value_consumer -------------------------------------------------------
ini_value_consumer::ini_value_consumer(abstract_ini_consumer* parent)
: super(parent) {
// nop
}
void ini_value_consumer::value_impl(config_value&& x) {
result = std::move(x);
}
// -- ini_category_consumer ----------------------------------------------------
ini_category_consumer::ini_category_consumer(ini_consumer* parent,
std::string category)
: super(parent),
category_(std::move(category)) {
CAF_ASSERT(parent != nullptr);
}
void ini_category_consumer::end_map() {
parent_->value_impl(config_value{std::move(xs_)});
}
void ini_category_consumer::key(std::string name) {
current_key = std::move(name);
}
void ini_category_consumer::value_impl(config_value&& x) {
// See whether there's an config_option associated to this category and key.
auto opt = dparent()->options_.qualified_name_lookup(category_, current_key);
if (opt == nullptr) {
// Simply store in config if no option was found.
xs_.emplace(std::move(current_key), std::move(x));
} else {
// Perform a type check to see whether this is a valid input.
if (opt->check(x) == none) {
opt->store(x);
xs_.emplace(std::move(current_key), std::move(x));
} else {
dparent()->warnings_.emplace_back(make_error(pec::type_mismatch));
}
}
}
ini_consumer* ini_category_consumer::dparent() {
// Safe, because our constructor only accepts ini_consumer parents.
return static_cast<ini_consumer*>(parent());
}
// -- ini_consumer -------------------------------------------------------------
ini_consumer::ini_consumer(config_option_set& options, config_map& cfg)
: options_(options),
cfg_(cfg) {
// nop
}
ini_category_consumer ini_consumer::begin_map() {
return {this, current_key};
}
void ini_consumer::key(std::string name) {
current_key = std::move(name);
}
void ini_consumer::value_impl(config_value&& x) {
auto dict = get_if<config_value::dictionary>(&x);
if (dict != nullptr && !dict->empty())
cfg_.emplace(std::move(current_key), std::move(*dict));
}
} // namespace detail
} // namespace caf
...@@ -16,9 +16,10 @@ ...@@ -16,9 +16,10 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/detail/parser/ec.hpp" #include "caf/pec.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/make_message.hpp"
namespace { namespace {
...@@ -37,22 +38,28 @@ constexpr const char* tbl[] = { ...@@ -37,22 +38,28 @@ constexpr const char* tbl[] = {
"exponent_underflow", "exponent_underflow",
"exponent_overflow", "exponent_overflow",
"type_mismatch", "type_mismatch",
"not_an_option",
"illegal_argument",
"missing_argument",
"illegal_category",
}; };
} // namespace <anonymous> } // namespace <anonymous>
namespace caf { namespace caf {
namespace detail {
namespace parser {
error make_error(ec code) { error make_error(pec code) {
return {static_cast<uint8_t>(code), atom("parser")}; return {static_cast<uint8_t>(code), atom("parser")};
} }
const char* to_string(ec x) { error make_error(pec code, size_t line, size_t column) {
return {static_cast<uint8_t>(code), atom("parser"),
make_message(static_cast<uint32_t>(line),
static_cast<uint32_t>(column))};
}
const char* to_string(pec x) {
return tbl[static_cast<uint8_t>(x)]; return tbl[static_cast<uint8_t>(x)];
} }
} // namespace parser
} // namespace detail
} // namespace caf } // namespace caf
...@@ -16,23 +16,12 @@ ...@@ -16,23 +16,12 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE config_option #define CAF_SUITE config_option
#include "caf/test/unit_test.hpp" #include "caf/test/unit_test.hpp"
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
// turn off several flags for overflows / sign conversion
#ifdef CAF_CLANG
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wconstant-conversion"
#elif defined(CAF_GCC)
#pragma GCC diagnostic ignored "-Woverflow"
#endif
using namespace caf; using namespace caf;
using std::string; using std::string;
...@@ -42,7 +31,6 @@ namespace { ...@@ -42,7 +31,6 @@ namespace {
constexpr const char* category = "category"; constexpr const char* category = "category";
constexpr const char* name = "name"; constexpr const char* name = "name";
constexpr const char* explanation = "explanation"; constexpr const char* explanation = "explanation";
constexpr size_t line = 0;
template<class T> template<class T>
constexpr int64_t overflow() { constexpr int64_t overflow() {
...@@ -55,36 +43,39 @@ constexpr int64_t underflow() { ...@@ -55,36 +43,39 @@ constexpr int64_t underflow() {
} }
template <class T> template <class T>
optional<T> read(config_value test_value) { optional<T> read(const std::string& arg) {
T output_value{}; auto co = make_config_option<T>(category, name, explanation);
auto co = make_config_option(output_value, category, name, explanation); auto res = config_value::parse(arg);
auto f = co->to_sink(); if (res && holds_alternative<T>(*res)) {
if (f(line, test_value, none)) CAF_CHECK_EQUAL(co.check(*res), none);
return output_value; return get<T>(*res);
}
return none; return none;
} }
// Unsigned integers. // Unsigned integers.
template <class T> template <class T>
void check_integer_options(std::true_type) { void check_integer_options(std::true_type) {
using std::to_string;
// Run tests for positive integers. // Run tests for positive integers.
T xzero = 0; T xzero = 0;
T xmax = std::numeric_limits<T>::max(); T xmax = std::numeric_limits<T>::max();
CAF_CHECK_EQUAL(read<T>(config_value{xzero}), xzero); CAF_CHECK_EQUAL(read<T>(to_string(xzero)), xzero);
CAF_CHECK_EQUAL(read<T>(config_value{xmax}), xmax); CAF_CHECK_EQUAL(read<T>(to_string(xmax)), xmax);
CAF_CHECK_EQUAL(read<T>(config_value{overflow<T>()}), none); CAF_CHECK_EQUAL(read<T>(to_string(overflow<T>())), none);
} }
// Signed integers. // Signed integers.
template <class T> template <class T>
void check_integer_options(std::false_type) { void check_integer_options(std::false_type) {
using std::to_string;
// Run tests for positive integers. // Run tests for positive integers.
std::true_type tk; std::true_type tk;
check_integer_options<T>(tk); check_integer_options<T>(tk);
// Run tests for negative integers. // Run tests for negative integers.
auto xmin = std::numeric_limits<T>::min(); auto xmin = std::numeric_limits<T>::min();
CAF_CHECK_EQUAL(read<T>(config_value{xmin}), xmin); CAF_CHECK_EQUAL(read<T>(to_string(xmin)), xmin);
CAF_CHECK_EQUAL(read<T>(config_value{underflow<T>()}), none); CAF_CHECK_EQUAL(read<T>(to_string(underflow<T>())), none);
} }
// only works with an integral types and double // only works with an integral types and double
...@@ -104,10 +95,10 @@ T unbox(optional<T> x) { ...@@ -104,10 +95,10 @@ T unbox(optional<T> x) {
} // namespace <anonymous> } // namespace <anonymous>
CAF_TEST(type_bool) { CAF_TEST(type_bool) {
CAF_CHECK_EQUAL(read<bool>(config_value{true}), true); CAF_CHECK_EQUAL(read<bool>("true"), true);
CAF_CHECK_EQUAL(read<bool>(config_value{false}), false); CAF_CHECK_EQUAL(read<bool>("false"), false);
CAF_CHECK_EQUAL(read<bool>(config_value{0}), none); CAF_CHECK_EQUAL(read<bool>("0"), none);
CAF_CHECK_EQUAL(read<bool>(config_value{1}), none); CAF_CHECK_EQUAL(read<bool>("1"), none);
} }
CAF_TEST(type int8_t) { CAF_TEST(type int8_t) {
...@@ -135,66 +126,41 @@ CAF_TEST(type uint32_t) { ...@@ -135,66 +126,41 @@ CAF_TEST(type uint32_t) {
} }
CAF_TEST(type uint64_t) { CAF_TEST(type uint64_t) {
CAF_CHECK_EQUAL(unbox(read<uint64_t>(config_value{0})), 0u); CAF_CHECK_EQUAL(unbox(read<uint64_t>("0")), 0u);
CAF_CHECK_EQUAL(read<uint64_t>(config_value{-1}), none); CAF_CHECK_EQUAL(read<uint64_t>("-1"), none);
} }
CAF_TEST(type int64_t) { CAF_TEST(type int64_t) {
CAF_CHECK_EQUAL(unbox(read<int64_t>(config_value{-1})), -1); CAF_CHECK_EQUAL(unbox(read<int64_t>("-1")), -1);
CAF_CHECK_EQUAL(unbox(read<int64_t>(config_value{0})), 0); CAF_CHECK_EQUAL(unbox(read<int64_t>("0")), 0);
CAF_CHECK_EQUAL(unbox(read<int64_t>(config_value{1})), 1); CAF_CHECK_EQUAL(unbox(read<int64_t>("1")), 1);
} }
CAF_TEST(type float) { CAF_TEST(type float) {
CAF_CHECK_EQUAL(unbox(read<float>(config_value{-1.0})), -1.0f); CAF_CHECK_EQUAL(unbox(read<float>("-1.0")), -1.0f);
CAF_CHECK_EQUAL(unbox(read<float>(config_value{-0.1})), -0.1f); CAF_CHECK_EQUAL(unbox(read<float>("-0.1")), -0.1f);
CAF_CHECK_EQUAL(read<float>(config_value{0}), none); CAF_CHECK_EQUAL(read<float>("0"), none);
CAF_CHECK_EQUAL(read<float>(config_value{"0.1"}), none); CAF_CHECK_EQUAL(read<float>("\"0.1\""), none);
} }
CAF_TEST(type double) { CAF_TEST(type double) {
CAF_CHECK_EQUAL(unbox(read<double>(config_value{-1.0})), -1.0); CAF_CHECK_EQUAL(unbox(read<double>("-1.0")), -1.0);
CAF_CHECK_EQUAL(unbox(read<double>(config_value{-0.1})), -0.1); CAF_CHECK_EQUAL(unbox(read<double>("-0.1")), -0.1);
CAF_CHECK_EQUAL(read<double>(config_value{0}), none); CAF_CHECK_EQUAL(read<double>("0"), none);
CAF_CHECK_EQUAL(read<double>(config_value{"0.1"}), none); CAF_CHECK_EQUAL(read<double>("\"0.1\""), none);
} }
CAF_TEST(type string) { CAF_TEST(type string) {
CAF_CHECK_EQUAL(unbox(read<string>(config_value{"foo"})), "foo"); CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo");
} }
CAF_TEST(type atom) { CAF_TEST(type atom) {
auto foo = atom("foo"); CAF_CHECK_EQUAL(unbox(read<atom_value>("'foo'")), atom("foo"));
CAF_CHECK_EQUAL(unbox(read<atom_value>(config_value{foo})), foo); CAF_CHECK_EQUAL(read<atom_value>("bar"), none);
CAF_CHECK_EQUAL(read<atom_value>(config_value{"bar"}), none);
} }
CAF_TEST(type timespan) { CAF_TEST(type timespan) {
timespan dur{500}; timespan dur{500};
CAF_CHECK_EQUAL(unbox(read<timespan>(config_value{dur})), dur); CAF_CHECK_EQUAL(unbox(read<timespan>("500ns")), dur);
}
template <class T>
std::string name_of() {
T x{};
config_option::type_name_visitor v;
return v(x);
}
CAF_TEST(type_names) {
CAF_CHECK_EQUAL((name_of<std::map<int, int>>()), "a dictionary");
CAF_CHECK_EQUAL(name_of<atom_value>(), "an atom_value");
CAF_CHECK_EQUAL(name_of<bool>(), "a boolean");
CAF_CHECK_EQUAL(name_of<double>(), "a double");
CAF_CHECK_EQUAL(name_of<float>(), "a float");
CAF_CHECK_EQUAL(name_of<int16_t>(), "a 16-bit integer");
CAF_CHECK_EQUAL(name_of<int32_t>(), "a 32-bit integer");
CAF_CHECK_EQUAL(name_of<int64_t>(), "a 64-bit integer");
CAF_CHECK_EQUAL(name_of<int8_t>(), "an 8-bit integer");
CAF_CHECK_EQUAL(name_of<std::vector<int>>(), "a list");
CAF_CHECK_EQUAL(name_of<string>(), "a string");
CAF_CHECK_EQUAL(name_of<uint16_t>(), "a 16-bit unsigned integer");
CAF_CHECK_EQUAL(name_of<uint32_t>(), "a 32-bit unsigned integer");
CAF_CHECK_EQUAL(name_of<uint64_t>(), "a 64-bit unsigned integer");
CAF_CHECK_EQUAL(name_of<uint8_t>(), "an 8-bit unsigned integer");
} }
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <map>
#include <string>
#include <vector>
#include "caf/config.hpp"
#define CAF_SUITE config_option_set
#include "caf/test/dsl.hpp"
#include "caf/config_option_set.hpp"
using std::string;
using std::vector;
using namespace caf;
namespace {
struct fixture {
config_option_set opts;
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(config_option_set_tests, fixture)
CAF_TEST(lookup) {
opts.add<int>("global", "opt1,1", "test option 1")
.add<float>("test", "opt2,2", "test option 2")
.add<bool>("test", "flag,fl3", "test flag");
CAF_CHECK_EQUAL(opts.size(), 3u);
CAF_MESSAGE("lookup by long name");
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("opt1"), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("global.opt1"), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("test.opt2"), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("test.flag"), nullptr);
CAF_MESSAGE("lookup by short name");
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('1'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('2'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('f'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('l'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('3'), nullptr);
}
CAF_TEST(parse with ref syncing) {
using ls = vector<string>; // list of strings
using ds = std::map<string, string>; // dictionary of strings
auto foo_i = 0;
auto foo_f = 0.f;
auto foo_b = false;
auto bar_s = string{};
auto bar_l = ls{};
auto bar_d = ds{};
opts.add<int>(foo_i, "foo", "i,i", "")
.add<float>(foo_f, "foo", "f,f", "")
.add<bool>(foo_b, "foo", "b,b", "")
.add<string>(bar_s, "bar", "s,s", "")
.add<vector<string>>(bar_l, "bar", "l,l", "")
.add<std::map<string, string>>(bar_d, "bar", "d,d", "");
std::map<std::string, config_value::dictionary> cfg;
vector<string> args{"-i42",
"-f",
"1e12",
"-shello",
"--bar.l=[\"hello\", \"world\"]",
"-d",
"{a=\"a\",b=\"b\"}",
"-b"};
CAF_MESSAGE("parse arguments");
auto res = opts.parse(cfg, args);
CAF_CHECK_EQUAL(res.first, pec::success);
if (res.second != args.end())
CAF_FAIL("parser stopped at: " << *res.second);
CAF_MESSAGE("verify referenced values");
CAF_CHECK_EQUAL(foo_i, 42);
CAF_CHECK_EQUAL(foo_f, 1e12);
CAF_CHECK_EQUAL(foo_b, true);
CAF_CHECK_EQUAL(bar_s, "hello");
CAF_CHECK_EQUAL(bar_l, ls({"hello", "world"}));
CAF_CHECK_EQUAL(bar_d, ds({{"a", "a"}, {"b", "b"}}));
CAF_MESSAGE("verify dictionary content");
CAF_CHECK_EQUAL(get<int>(cfg, "foo.i"), 42);
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/detail/bounds_checker.hpp" #include "caf/detail/bounds_checker.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/pec.hpp"
#include "caf/variant.hpp" #include "caf/variant.hpp"
using namespace std; using namespace std;
...@@ -236,11 +236,10 @@ CAF_TEST(unsuccessful parsing) { ...@@ -236,11 +236,10 @@ CAF_TEST(unsuccessful parsing) {
CAF_FAIL("assumed an error but got a result"); CAF_FAIL("assumed an error but got a result");
return std::move(x.error()); return std::move(x.error());
}; };
using detail::parser::ec; CAF_CHECK_EQUAL(parse("10msb"), pec::trailing_character);
CAF_CHECK_EQUAL(parse("10msb"), ec::trailing_character); CAF_CHECK_EQUAL(parse("10foo"), pec::trailing_character);
CAF_CHECK_EQUAL(parse("10foo"), ec::trailing_character); CAF_CHECK_EQUAL(parse("[1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("[1,"), ec::unexpected_eof); CAF_CHECK_EQUAL(parse("{a=,"), pec::unexpected_character);
CAF_CHECK_EQUAL(parse("{a=,"), ec::unexpected_character); CAF_CHECK_EQUAL(parse("{a=1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=1,"), ec::unexpected_eof); CAF_CHECK_EQUAL(parse("{a=1 b=2}"), pec::unexpected_character);
CAF_CHECK_EQUAL(parse("{a=1 b=2}"), ec::unexpected_character);
} }
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE ini_consumer
#include "caf/test/dsl.hpp"
#include "caf/detail/ini_consumer.hpp"
#include "caf/detail/parser/read_ini.hpp"
using std::string;
using namespace caf;
// List-of-strings.
using ls = std::vector<std::string>;
namespace {
const char test_ini[] = R"(
[global]
is_server=true
port=4242
nodes=["sun", "venus", ]
[logger]
file-name = "foobar.ini" ; our file name
[scheduler] ; more settings
timing = 2us ; using microsecond resolution
impl = 'foo';some atom
)";
struct fixture {
detail::parser::state<std::string::const_iterator> res;
config_option_set options;
config_option_set::config_map config;
fixture() {
options.add<bool>("global", "is_server")
.add<uint16_t>("global", "port")
.add<ls>("global", "nodes")
.add<string>("logger", "file-name")
.add<int>("scheduler", "padding")
.add<timespan>("scheduler", "timing")
.add<atom_value>("scheduler", "impl");
}
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(ini_consumer_tests, fixture)
CAF_TEST(ini_value_consumer) {
std::string str = R"("hello world")";
detail::ini_value_consumer consumer;
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini_value(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<string>(consumer.result), "hello world");
}
CAF_TEST(ini_consumer) {
std::string str = test_ini;
detail::ini_consumer consumer{options, config};
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<bool>(config, "global.is_server"), true);
CAF_CHECK_EQUAL(get<uint16_t>(config, "global.port"), 4242u);
CAF_CHECK_EQUAL(get<ls>(config, "global.nodes"), ls({"sun", "venus"}));
CAF_CHECK_EQUAL(get<string>(config, "logger.file-name"), "foobar.ini");
CAF_CHECK_EQUAL(get<timespan>(config, "scheduler.timing"), timespan(2000));
CAF_CHECK_EQUAL(get<atom_value>(config, "scheduler.impl"), atom("foo"));
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
using namespace caf; using namespace caf;
using detail::parser::ec;
namespace { namespace {
struct atom_parser_consumer { struct atom_parser_consumer {
...@@ -39,7 +37,7 @@ struct atom_parser_consumer { ...@@ -39,7 +37,7 @@ struct atom_parser_consumer {
} }
}; };
using res_t = variant<ec, atom_value>; using res_t = variant<pec, atom_value>;
struct atom_parser { struct atom_parser {
res_t operator()(std::string str) { res_t operator()(std::string str) {
...@@ -48,7 +46,7 @@ struct atom_parser { ...@@ -48,7 +46,7 @@ struct atom_parser {
res.i = str.begin(); res.i = str.begin();
res.e = str.end(); res.e = str.end();
detail::parser::read_atom(res, f); detail::parser::read_atom(res, f);
if (res.code == ec::success) if (res.code == pec::success)
return f.x; return f.x;
return res.code; return res.code;
} }
...@@ -79,11 +77,11 @@ CAF_TEST(non-empty atom) { ...@@ -79,11 +77,11 @@ CAF_TEST(non-empty atom) {
} }
CAF_TEST(invalid atoms) { CAF_TEST(invalid atoms) {
CAF_CHECK_EQUAL(p("'abc"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("'abc"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("'ab\nc'"), ec::unexpected_newline); CAF_CHECK_EQUAL(p("'ab\nc'"), pec::unexpected_newline);
CAF_CHECK_EQUAL(p("abc"), ec::unexpected_character); CAF_CHECK_EQUAL(p("abc"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("'abc' def"), ec::trailing_character); CAF_CHECK_EQUAL(p("'abc' def"), pec::trailing_character);
CAF_CHECK_EQUAL(p("'12345678901'"), ec::too_many_characters); CAF_CHECK_EQUAL(p("'12345678901'"), pec::too_many_characters);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
using namespace caf; using namespace caf;
using detail::parser::ec;
namespace { namespace {
struct bool_parser_consumer { struct bool_parser_consumer {
...@@ -39,7 +37,7 @@ struct bool_parser_consumer { ...@@ -39,7 +37,7 @@ struct bool_parser_consumer {
} }
}; };
using res_t = variant<ec, bool>; using res_t = variant<pec, bool>;
struct bool_parser { struct bool_parser {
res_t operator()(std::string str) { res_t operator()(std::string str) {
...@@ -48,7 +46,7 @@ struct bool_parser { ...@@ -48,7 +46,7 @@ struct bool_parser {
res.i = str.begin(); res.i = str.begin();
res.e = str.end(); res.e = str.end();
detail::parser::read_bool(res, f); detail::parser::read_bool(res, f);
if (res.code == ec::success) if (res.code == pec::success)
return f.x; return f.x;
return res.code; return res.code;
} }
...@@ -68,18 +66,18 @@ CAF_TEST(valid booleans) { ...@@ -68,18 +66,18 @@ CAF_TEST(valid booleans) {
} }
CAF_TEST(invalid booleans) { CAF_TEST(invalid booleans) {
CAF_CHECK_EQUAL(p(""), ec::unexpected_eof); CAF_CHECK_EQUAL(p(""), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("t"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("t"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("tr"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("tr"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("tru"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("tru"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p(" true"), ec::unexpected_character); CAF_CHECK_EQUAL(p(" true"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("f"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("f"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("fa"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("fa"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("fal"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("fal"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("fals"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("fals"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p(" false"), ec::unexpected_character); CAF_CHECK_EQUAL(p(" false"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("tr\nue"), ec::unexpected_newline); CAF_CHECK_EQUAL(p("tr\nue"), pec::unexpected_newline);
CAF_CHECK_EQUAL(p("trues"), ec::trailing_character); CAF_CHECK_EQUAL(p("trues"), pec::trailing_character);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -35,6 +35,10 @@ using log_type = std::vector<std::string>; ...@@ -35,6 +35,10 @@ using log_type = std::vector<std::string>;
struct test_consumer { struct test_consumer {
log_type log; log_type log;
test_consumer() = default;
test_consumer(const test_consumer&) = delete;
test_consumer& operator=(const test_consumer&) = delete;
test_consumer& begin_map() { test_consumer& begin_map() {
log.emplace_back("{"); log.emplace_back("{");
return *this; return *this;
...@@ -83,7 +87,7 @@ struct fixture { ...@@ -83,7 +87,7 @@ struct fixture {
res.i = str.begin(); res.i = str.begin();
res.e = str.end(); res.e = str.end();
detail::parser::read_ini(res, f); detail::parser::read_ini(res, f);
if (res.code == detail::parser::ec::success != expect_success) if (res.code == pec::success != expect_success)
CAF_MESSAGE("unexpected parser result state: " << res.code); CAF_MESSAGE("unexpected parser result state: " << res.code);
return std::move(f.log); return std::move(f.log);
} }
......
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
using namespace caf; using namespace caf;
using detail::parser::ec;
namespace { namespace {
struct numbers_parser_consumer { struct numbers_parser_consumer {
...@@ -43,7 +41,7 @@ struct numbers_parser_consumer { ...@@ -43,7 +41,7 @@ struct numbers_parser_consumer {
}; };
struct res_t { struct res_t {
variant<ec, double, int64_t> val; variant<pec, double, int64_t> val;
template <class T> template <class T>
res_t(T&& x) : val(std::forward<T>(x)) { res_t(T&& x) : val(std::forward<T>(x)) {
// nop // nop
...@@ -57,8 +55,8 @@ bool operator==(const res_t& x, const res_t& y) { ...@@ -57,8 +55,8 @@ bool operator==(const res_t& x, const res_t& y) {
caf::test::equal_to f; caf::test::equal_to f;
using caf::get; using caf::get;
using caf::holds_alternative; using caf::holds_alternative;
if (holds_alternative<ec>(x.val)) if (holds_alternative<pec>(x.val))
return f(get<ec>(x.val), get<ec>(y.val)); return f(get<pec>(x.val), get<pec>(y.val));
if (holds_alternative<double>(x.val)) if (holds_alternative<double>(x.val))
return f(get<double>(x.val), get<double>(y.val)); return f(get<double>(x.val), get<double>(y.val));
return f(get<int64_t>(x.val), get<int64_t>(y.val)); return f(get<int64_t>(x.val), get<int64_t>(y.val));
...@@ -71,7 +69,7 @@ struct numbers_parser { ...@@ -71,7 +69,7 @@ struct numbers_parser {
res.i = str.begin(); res.i = str.begin();
res.e = str.end(); res.e = str.end();
detail::parser::read_number(res, f); detail::parser::read_number(res, f);
if (res.code == ec::success) if (res.code == pec::success)
return f.x; return f.x;
return res.code; return res.code;
} }
...@@ -126,7 +124,7 @@ CAF_TEST(octal numbers) { ...@@ -126,7 +124,7 @@ CAF_TEST(octal numbers) {
CHECK_NUMBER(-00); CHECK_NUMBER(-00);
CHECK_NUMBER(-0123); CHECK_NUMBER(-0123);
// invalid numbers // invalid numbers
CAF_CHECK_EQUAL(p("018"), ec::trailing_character); CAF_CHECK_EQUAL(p("018"), pec::trailing_character);
} }
CAF_TEST(decimal numbers) { CAF_TEST(decimal numbers) {
...@@ -147,11 +145,11 @@ CAF_TEST(hexadecimal numbers) { ...@@ -147,11 +145,11 @@ CAF_TEST(hexadecimal numbers) {
CHECK_NUMBER(-0x123); CHECK_NUMBER(-0x123);
CHECK_NUMBER(-0xaf01); CHECK_NUMBER(-0xaf01);
// invalid numbers // invalid numbers
CAF_CHECK_EQUAL(p("0xFG"), ec::trailing_character); CAF_CHECK_EQUAL(p("0xFG"), pec::trailing_character);
CAF_CHECK_EQUAL(p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), CAF_CHECK_EQUAL(p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
ec::integer_overflow); pec::integer_overflow);
CAF_CHECK_EQUAL(p("-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), CAF_CHECK_EQUAL(p("-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
ec::integer_underflow); pec::integer_underflow);
} }
CAF_TEST(floating point numbers) { CAF_TEST(floating point numbers) {
...@@ -196,8 +194,8 @@ CAF_TEST(integer mantissa with negative exponent) { ...@@ -196,8 +194,8 @@ CAF_TEST(integer mantissa with negative exponent) {
CHECK_NUMBER(1e-5); CHECK_NUMBER(1e-5);
CHECK_NUMBER(1e-6); CHECK_NUMBER(1e-6);
// invalid numbers // invalid numbers
CAF_CHECK_EQUAL(p("-9.9999e-e511"), ec::unexpected_character); CAF_CHECK_EQUAL(p("-9.9999e-e511"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("-9.9999e-511"), ec::exponent_underflow); CAF_CHECK_EQUAL(p("-9.9999e-511"), pec::exponent_underflow);
} }
CAF_TEST(fractional mantissa with positive exponent) { CAF_TEST(fractional mantissa with positive exponent) {
......
...@@ -29,8 +29,6 @@ ...@@ -29,8 +29,6 @@
using namespace caf; using namespace caf;
using namespace std::chrono; using namespace std::chrono;
using detail::parser::ec;
namespace { namespace {
struct number_or_timespan_parser_consumer { struct number_or_timespan_parser_consumer {
...@@ -42,7 +40,7 @@ struct number_or_timespan_parser_consumer { ...@@ -42,7 +40,7 @@ struct number_or_timespan_parser_consumer {
}; };
struct res_t { struct res_t {
variant<ec, double, int64_t, timespan> val; variant<pec, double, int64_t, timespan> val;
template <class T> template <class T>
res_t(T&& x) : val(std::forward<T>(x)) { res_t(T&& x) : val(std::forward<T>(x)) {
// nop // nop
...@@ -60,8 +58,8 @@ bool operator==(const res_t& x, const res_t& y) { ...@@ -60,8 +58,8 @@ bool operator==(const res_t& x, const res_t& y) {
caf::test::equal_to f; caf::test::equal_to f;
using caf::get; using caf::get;
using caf::holds_alternative; using caf::holds_alternative;
if (holds_alternative<ec>(x.val)) if (holds_alternative<pec>(x.val))
return f(get<ec>(x.val), get<ec>(y.val)); return f(get<pec>(x.val), get<pec>(y.val));
if (holds_alternative<double>(x.val)) if (holds_alternative<double>(x.val))
return f(get<double>(x.val), get<double>(y.val)); return f(get<double>(x.val), get<double>(y.val));
if (holds_alternative<int64_t>(x.val)) if (holds_alternative<int64_t>(x.val))
...@@ -76,7 +74,7 @@ struct number_or_timespan_parser { ...@@ -76,7 +74,7 @@ struct number_or_timespan_parser {
res.i = str.begin(); res.i = str.begin();
res.e = str.end(); res.e = str.end();
detail::parser::read_number_or_timespan(res, f); detail::parser::read_number_or_timespan(res, f);
if (res.code == ec::success) if (res.code == pec::success)
return f.x; return f.x;
return res.code; return res.code;
} }
...@@ -117,20 +115,20 @@ CAF_TEST(valid numbers and timespans) { ...@@ -117,20 +115,20 @@ CAF_TEST(valid numbers and timespans) {
} }
CAF_TEST(invalid timespans) { CAF_TEST(invalid timespans) {
CAF_CHECK_EQUAL(p("12.3s"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3s"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3n"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3n"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ns"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3ns"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3m"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3m"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ms"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3ms"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3n"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3n"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ns"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3ns"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3mi"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3mi"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3min"), ec::fractional_timespan); CAF_CHECK_EQUAL(p("12.3min"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("123ss"), ec::trailing_character); CAF_CHECK_EQUAL(p("123ss"), pec::trailing_character);
CAF_CHECK_EQUAL(p("123m"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("123m"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("123mi"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("123mi"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("123u"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("123u"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("123n"), ec::unexpected_eof); CAF_CHECK_EQUAL(p("123n"), pec::unexpected_eof);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
......
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
using namespace caf; using namespace caf;
using detail::parser::ec;
namespace { namespace {
struct string_parser_consumer { struct string_parser_consumer {
...@@ -39,7 +37,7 @@ struct string_parser_consumer { ...@@ -39,7 +37,7 @@ struct string_parser_consumer {
} }
}; };
using res_t = variant<ec, std::string>; using res_t = variant<pec, std::string>;
struct string_parser { struct string_parser {
res_t operator()(std::string str) { res_t operator()(std::string str) {
...@@ -48,7 +46,7 @@ struct string_parser { ...@@ -48,7 +46,7 @@ struct string_parser {
res.i = str.begin(); res.i = str.begin();
res.e = str.end(); res.e = str.end();
detail::parser::read_string(res, f); detail::parser::read_string(res, f);
if (res.code == ec::success) if (res.code == pec::success)
return f.x; return f.x;
return res.code; return res.code;
} }
...@@ -93,11 +91,11 @@ CAF_TEST(string with escaped characters) { ...@@ -93,11 +91,11 @@ CAF_TEST(string with escaped characters) {
} }
CAF_TEST(invalid strings) { CAF_TEST(invalid strings) {
CAF_CHECK_EQUAL(p(R"("abc)"), ec::unexpected_eof); CAF_CHECK_EQUAL(p(R"("abc)"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("\"ab\nc\""), ec::unexpected_newline); CAF_CHECK_EQUAL(p("\"ab\nc\""), pec::unexpected_newline);
CAF_CHECK_EQUAL(p(R"("foo \i bar")"), ec::illegal_escape_sequence); CAF_CHECK_EQUAL(p(R"("foo \i bar")"), pec::illegal_escape_sequence);
CAF_CHECK_EQUAL(p(R"(foo)"), ec::unexpected_character); CAF_CHECK_EQUAL(p(R"(foo)"), pec::unexpected_character);
CAF_CHECK_EQUAL(p(R"("abc" def)"), ec::trailing_character); CAF_CHECK_EQUAL(p(R"("abc" def)"), pec::trailing_character);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
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