Unverified Commit 9085748e authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #882

Fix type-specific parsing of config options
parents 097df535 e03beeb8
......@@ -84,6 +84,7 @@ set(LIBCAF_CORE_SRCS
src/monitorable_actor.cpp
src/node_id.cpp
src/outbound_path.cpp
src/parse.cpp
src/pec.cpp
src/pretty_type_name.cpp
src/private_thread.cpp
......
......@@ -20,6 +20,7 @@
#include <chrono>
#include <cstdint>
#include <iterator>
#include <map>
#include <string>
#include <type_traits>
......@@ -177,35 +178,28 @@ private:
}
template <class T>
detail::enable_if_t<detail::is_list_like<detail::decay_t<T>>::value
&& !std::is_same<detail::decay_t<T>, list>::value>
set(T&& xs) {
// Move values from the old list into the new list if `xs` is an rvalue.
using namespace detail;
using xs_type = decltype(xs);
using value_type = typename decay_t<T>::value_type;
using fwd_type = conditional_t<std::is_rvalue_reference<xs_type>::value,
value_type&&, const value_type&>;
auto& lst = as_list();
lst.clear();
for (auto& x : xs)
lst.emplace_back(config_value{static_cast<fwd_type>(x)});
}
template <class T>
detail::enable_if_t<detail::is_map_like<detail::decay_t<T>>::value
&& !std::is_same<detail::decay_t<T>, dictionary>::value>
set(T&& xs) {
// Move values from the old list into the new list if `xs` is an rvalue.
using namespace detail;
using xs_type = decltype(xs);
using mapped_type = typename decay_t<T>::mapped_type;
using fwd_type = conditional_t<std::is_rvalue_reference<xs_type>::value,
mapped_type&&, const mapped_type&>;
void set_range(T& xs, std::true_type) {
auto& dict = as_dictionary();
dict.clear();
for (auto& kvp : xs)
dict.emplace(kvp.first, static_cast<fwd_type>(kvp.second));
dict.emplace(kvp.first, std::move(kvp.second));
}
template <class T>
void set_range(T& xs, std::false_type) {
auto& ls = as_list();
ls.clear();
ls.insert(ls.end(), std::make_move_iterator(xs.begin()),
std::make_move_iterator(xs.end()));
}
template <class T>
detail::enable_if_t<detail::is_iterable<T>::value
&& !detail::is_one_of<T, string, list, dictionary>::value>
set(T xs) {
using value_type = typename T::value_type;
detail::bool_token<detail::is_pair<value_type>::value> is_map_type;
set_range(xs, is_map_type);
}
template <class T>
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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 <utility>
namespace caf {
namespace detail {
template <class T>
class consumer {
public:
using value_type = T;
explicit consumer(T& x) : x_(x) {
// nop
}
void value(T&& y) {
x_ = std::move(y);
}
private:
T& x_;
};
template <class T>
consumer<T> make_consumer(T& x) {
return consumer<T>{x};
}
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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 <cstdint>
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
#include "caf/detail/parser/state.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/error.hpp"
#include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/string_view.hpp"
#include "caf/unit.hpp"
namespace caf {
namespace detail {
using parse_state = parser::state<string_view::iterator>;
// -- boolean type -------------------------------------------------------------
void parse(parse_state& ps, bool& x);
// -- signed integer types -----------------------------------------------------
void parse(parse_state& ps, int8_t& x);
void parse(parse_state& ps, int16_t& x);
void parse(parse_state& ps, int32_t& x);
void parse(parse_state& ps, int64_t& x);
// -- unsigned integer types ---------------------------------------------------
void parse(parse_state& ps, uint8_t& x);
void parse(parse_state& ps, uint16_t& x);
void parse(parse_state& ps, uint32_t& x);
void parse(parse_state& ps, uint64_t& x);
// -- non-fixed size integer types ---------------------------------------------
template <class T>
detail::enable_if_t<std::is_integral<T>::value> parse(parse_state& ps, T& x) {
using squashed_type = squashed_int_t<T>;
return parse(ps, reinterpret_cast<squashed_type&>(x));
}
// -- floating point types -----------------------------------------------------
void parse(parse_state& ps, float& x);
void parse(parse_state& ps, double& x);
// -- CAF types ----------------------------------------------------------------
void parse(parse_state& ps, timespan& x);
void parse(parse_state& ps, atom_value& x);
void parse(parse_state& ps, uri& x);
// -- STL types ----------------------------------------------------------------
void parse(parse_state& ps, std::string& x);
// -- container types ----------------------------------------------------------
template <class First, class Second>
void parse(parse_state& ps, std::pair<First, Second>& kvp) {
parse(ps, kvp.first);
if (ps.code > pec::trailing_character)
return;
if (!ps.consume('=')) {
ps.code = pec::unexpected_character;
return;
}
parse(ps, kvp.second);
}
template <class T>
enable_if_tt<is_iterable<T>> parse(parse_state& ps, T& xs) {
using value_type = deconst_kvp_t<typename T::value_type>;
static constexpr auto is_map_type = is_pair<value_type>::value;
static constexpr auto opening_char = is_map_type ? '{' : '[';
static constexpr auto closing_char = is_map_type ? '}' : ']';
auto out = std::inserter(xs, xs.end());
// List/map using [] or {} notation.
if (ps.consume(opening_char)) {
do {
if (ps.consume(closing_char)) {
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
return;
}
value_type tmp;
parse(ps, tmp);
if (ps.code > pec::trailing_character)
return;
*out++ = std::move(tmp);
} while (ps.consume(','));
if (ps.consume(closing_char)) {
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
} else {
ps.code = pec::unexpected_character;
}
return;
}
// An empty string simply results in an empty list/map.
if (ps.at_end())
return;
// List/map without [] or {}.
do {
value_type tmp;
parse(ps, tmp);
if (ps.code > pec::trailing_character)
return;
*out++ = std::move(tmp);
} while (ps.consume(','));
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
// -- convenience functions ----------------------------------------------------
template <class T>
error parse(string_view str, T& x) {
parse_state ps{str.begin(), str.end()};
parse(ps, x);
if (ps.code == pec::success)
return none;
return make_error(ps.code, str);
}
} // namespace detail
} // namespace caf
......@@ -41,13 +41,13 @@ namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
void read_atom(state<Iterator, Sentinel>& ps, Consumer&& consumer,
bool accept_unquoted = false) {
size_t pos = 0;
char buf[11];
memset(buf, 0, sizeof(buf));
auto is_legal = [](char c) {
return isalnum(c) || c == '_' || c == ' ';
};
auto is_legal = [](char c) { return isalnum(c) || c == '_' || c == ' '; };
auto is_legal_no_ws = [](char c) { return isalnum(c) || c == '_'; };
auto append = [&](char c) {
if (pos == sizeof(buf) - 1)
return false;
......@@ -58,10 +58,12 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
if (ps.code <= pec::trailing_character)
consumer.value(atom(buf));
});
// clang-format off
start();
state(init) {
transition(init, " \t")
transition(read_chars, '\'')
epsilon_if(accept_unquoted, read_unquoted_chars, is_legal)
}
state(read_chars) {
transition(done, '\'')
......@@ -70,7 +72,11 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
term_state(done) {
transition(done, " \t")
}
term_state(read_unquoted_chars) {
transition(read_unquoted_chars, is_legal_no_ws, append(ch), pec::too_many_characters)
}
fin();
// clang-format on
}
} // namespace parser
......
......@@ -38,11 +38,11 @@ namespace parser {
/// Reads a boolean.
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;
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
consumer.value(res);
consumer.value(std::move(res));
});
start();
state(init) {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 <cstdint>
#include <type_traits>
#include "caf/config.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace caf {
namespace detail {
namespace parser {
/// Reads a floating point number (`float` or `double`).
/// @param ps The parser state.
/// @param consumer Sink for generated values.
/// @param start_value Allows another parser to pre-initialize this parser with
/// the pre-decimal value.
template <class Iterator, class Sentinel, class Consumer, class ValueType>
void read_floating_point(state<Iterator, Sentinel>& ps, Consumer&& consumer,
optional<ValueType> start_value,
bool negative = false) {
// Any exponent larger than 511 always overflows.
static constexpr int max_double_exponent = 511;
// We assume a simple integer until proven wrong.
enum sign_t { plus, minus };
sign_t sign;
ValueType result;
if (start_value == none) {
sign = plus;
result = 0;
} else if (*start_value < 0) {
sign = minus;
result = -*start_value;
} else if (negative) {
sign = minus;
result = *start_value;
} else {
sign = plus;
result = *start_value;
}
// Adjusts our mantissa, e.g., 1.23 becomes 123 with a dec_exp of -2.
auto dec_exp = 0;
// Exponent part of a floating point literal.
auto exp = 0;
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
// Compute final floating point number.
// 1) Fix the exponent.
exp += dec_exp;
// 2) Check whether exponent is in valid range.
if (exp < -max_double_exponent) {
ps.code = pec::exponent_underflow;
return;
}
if (exp > max_double_exponent) {
ps.code = pec::exponent_overflow;
return;
}
// 3) Scale result.
// Pre-computed powers of 10 for the scaling loop.
static double powerTable[] = {1e1, 1e2, 1e4, 1e8, 1e16,
1e32, 1e64, 1e128, 1e256};
auto i = 0;
if (exp < 0) {
for (auto n = -exp; n != 0; n >>= 1, ++i)
if (n & 0x01)
result /= powerTable[i];
} else {
for (auto n = exp; n != 0; n >>= 1, ++i)
if (n & 0x01)
result *= powerTable[i];
}
// 4) Fix sign and call consumer.
consumer.value(sign == plus ? result : -result);
}
});
// Reads the a decimal place.
auto rd_decimal = [&](char c) {
--dec_exp;
return add_ascii<10>(result, c);
};
// clang-format off
// Definition of our parser FSM.
start();
unstable_state(init) {
epsilon_if(start_value == none, regular_init)
epsilon(after_dec)
}
state(regular_init) {
transition(regular_init, " \t")
transition(has_sign, '+')
transition(has_sign, '-', sign = minus)
epsilon(has_sign)
}
// "+" or "-" alone aren't numbers.
state(has_sign) {
transition(leading_dot, '.')
transition(zero, '0')
epsilon(dec)
}
term_state(zero) {
transition(trailing_dot, '.')
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(dec) {
transition(dec, decimal_chars, add_ascii<10>(result, ch),
pec::integer_overflow)
epsilon(after_dec)
}
state(after_dec) {
transition(has_e, "eE")
transition(trailing_dot, '.')
}
// ".", "+.", etc. aren't valid numbers, so this state isn't terminal.
state(leading_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
}
// "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) {
epsilon(after_dot)
}
// Read the decimal part of a mantissa.
term_state(after_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
transition(has_e, "eE")
}
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
// terminal.
state(has_e) {
transition(has_plus_after_e, '+')
transition(has_minus_after_e, '-')
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
state(has_plus_after_e) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
state(has_minus_after_e) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
pec::exponent_underflow)
}
// Read a positive exponent.
term_state(pos_exp) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
// Read a negative exponent.
term_state(neg_exp) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
pec::exponent_underflow)
}
fin();
// clang-format on
}
template <class Iterator, class Sentinel, class Consumer>
void read_floating_point(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using consumer_type = typename std::decay<Consumer>::type;
using value_type = typename consumer_type::value_type;
return read_floating_point(ps, consumer, optional<value_type>{});
}
} // namespace parser
} // namespace detail
} // namespace caf
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
......@@ -21,10 +21,11 @@
#include <cstdint>
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
......@@ -42,87 +43,32 @@ namespace parser {
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
// Any exponent larger than 511 always overflows.
static constexpr int max_double_exponent = 511;
// We assume a simple integer until proven wrong.
enum result_type_t { integer, positive_double, negative_double };
auto result_type = integer;
// Adjusts our mantissa, e.g., 1.23 becomes 123 with a dec_exp of -2.
auto dec_exp = 0;
// Exponent part of a floating point literal.
auto exp = 0;
// Our result when reading a floating point number.
auto dbl_res = 0.;
// Our result when reading an integer number.
int64_t int_res = 0;
int64_t result = 0;
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
if (result_type == integer) {
consumer.value(int_res);
return;
}
// Compute final floating point number.
// 1) Fix the exponent.
exp += dec_exp;
// 2) Check whether exponent is in valid range.
if (exp < -max_double_exponent) {
ps.code = pec::exponent_underflow;
return;
}
if (exp > max_double_exponent) {
ps.code = pec::exponent_overflow;
return;
}
// 3) Scale result.
// Pre-computed powers of 10 for the scaling loop.
static double powerTable[] = {1e1, 1e2, 1e4, 1e8, 1e16,
1e32, 1e64, 1e128, 1e256};
auto i = 0;
if (exp < 0) {
for (auto n = -exp; n != 0; n >>= 1, ++i)
if (n & 0x01)
dbl_res /= powerTable[i];
} else {
for (auto n = exp; n != 0; n >>= 1, ++i)
if (n & 0x01)
dbl_res *= powerTable[i];
}
// 4) Fix sign and call consumer.
consumer.value(result_type == positive_double ? dbl_res : -dbl_res);
}
if (ps.code <= pec::trailing_character)
consumer.value(result);
});
// Switches from parsing an integer to parsing a double.
auto ch_res = [&](result_type_t x) {
CAF_ASSERT(result_type == integer);
result_type = x;
// We parse doubles as positive number and restore the sign in `g`.
dbl_res = result_type == negative_double
? -static_cast<double>(int_res)
: static_cast<double>(int_res);
};
// Reads the a decimal place.
auto rd_decimal = [&](char c) {
--dec_exp;
return add_ascii<10>(dbl_res, c);
};
// clang-format off
// Definition of our parser FSM.
start();
state(init) {
transition(init, " \t")
transition(has_plus, '+')
transition(has_minus, '-')
transition(pos_zero, '0')
epsilon(has_plus)
}
// "+" or "-" alone aren't numbers.
state(has_plus) {
transition(leading_dot, '.', ch_res(positive_double))
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}),
done, '.', g.disable())
transition(pos_zero, '0')
epsilon(pos_dec)
}
state(has_minus) {
transition(leading_dot, '.', ch_res(negative_double))
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}, true),
done, '.', g.disable())
transition(neg_zero, '0')
epsilon(neg_dec)
}
......@@ -130,13 +76,15 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
term_state(pos_zero) {
transition(start_pos_bin, "bB")
transition(start_pos_hex, "xX")
transition(trailing_dot, '.', ch_res(positive_double))
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}),
done, '.', g.disable())
epsilon(pos_oct)
}
term_state(neg_zero) {
transition(start_neg_bin, "bB")
transition(start_neg_hex, "xX")
transition(trailing_dot, '.', ch_res(negative_double))
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}, true),
done, '.', g.disable())
epsilon(neg_oct)
}
// Binary integers.
......@@ -144,27 +92,27 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(pos_bin)
}
term_state(pos_bin) {
transition(pos_bin, "01", add_ascii<2>(int_res, ch), pec::integer_overflow)
transition(pos_bin, "01", add_ascii<2>(result, ch), pec::integer_overflow)
}
state(start_neg_bin) {
epsilon(neg_bin)
}
term_state(neg_bin) {
transition(neg_bin, "01", sub_ascii<2>(int_res, ch), pec::integer_underflow)
transition(neg_bin, "01", sub_ascii<2>(result, ch), pec::integer_underflow)
}
// Octal integers.
state(start_pos_oct) {
epsilon(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>(result, ch),
pec::integer_overflow)
}
state(start_neg_oct) {
epsilon(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>(result, ch),
pec::integer_underflow)
}
// Hexal integers.
......@@ -172,70 +120,39 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(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>(result, ch),
pec::integer_overflow)
}
state(start_neg_hex) {
epsilon(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>(result, ch),
pec::integer_underflow)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) {
transition(pos_dec, decimal_chars, add_ascii<10>(int_res, ch),
transition(pos_dec, decimal_chars, add_ascii<10>(result, ch),
pec::integer_overflow)
transition(has_e, "eE", ch_res(positive_double))
transition(trailing_dot, '.', ch_res(positive_double))
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}),
done, "eE", g.disable())
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}),
done, '.', g.disable())
}
// Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) {
transition(neg_dec, decimal_chars, sub_ascii<10>(int_res, ch),
transition(neg_dec, decimal_chars, sub_ascii<10>(result, ch),
pec::integer_underflow)
transition(has_e, "eE", ch_res(negative_double))
transition(trailing_dot, '.', ch_res(negative_double))
}
// ".", "+.", etc. aren't valid numbers, so this state isn't terminal.
state(leading_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
}
// "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) {
epsilon(after_dot)
}
// Read the decimal part of a mantissa.
term_state(after_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
transition(has_e, "eE")
}
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
// terminal.
state(has_e) {
transition(has_plus_after_e, '+')
transition(has_minus_after_e, '-')
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
state(has_plus_after_e) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
state(has_minus_after_e) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
pec::exponent_underflow)
}
// Read a positive exponent.
term_state(pos_exp) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
// Read a negative exponent.
term_state(neg_exp) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
pec::exponent_underflow)
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}, true),
done, "eE", g.disable())
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}, true),
done, '.', g.disable())
}
term_state(done) {
// nop
}
fin();
// clang-format on
}
} // namespace parser
......
......@@ -26,6 +26,7 @@
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/parser/read_timespan.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/none.hpp"
......@@ -57,23 +58,19 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
interim = x;
}
};
optional<timespan> res;
interim_consumer ic;
auto has_int = [&] { return holds_alternative<int64_t>(ic.interim); };
auto has_dbl = [&] { return holds_alternative<double>(ic.interim); };
auto get_int = [&] { return get<int64_t>(ic.interim); };
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
if (res != none) {
consumer.value(*res);
} else if (!holds_alternative<none_t>(ic.interim)) {
if (has_int())
consumer.value(get_int());
else
if (has_dbl())
consumer.value(get<double>(ic.interim));
}
else if (has_int())
consumer.value(get_int());
}
});
// clang-format off
start();
state(init) {
fsm_epsilon(read_number(ps, ic), has_number)
......@@ -83,31 +80,17 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
epsilon_if(has_dbl(), has_double)
}
term_state(has_double) {
error_transition(pec::fractional_timespan, "unms")
error_transition(pec::fractional_timespan, "unmsh")
}
term_state(has_integer) {
transition(have_u, 'u')
transition(have_n, 'n')
transition(have_m, 'm')
transition(done, 's', res = seconds(get_int()))
}
state(have_u) {
transition(done, 's', res = microseconds(get_int()))
}
state(have_n) {
transition(done, 's', res = nanoseconds(get_int()))
}
state(have_m) {
transition(have_mi, 'i')
transition(done, 's', res = milliseconds(get_int()))
}
state(have_mi) {
transition(done, 'n', res = minutes(get_int()))
fsm_epsilon(read_timespan(ps, consumer, get_int()),
done, "unmsh", g.disable())
}
term_state(done) {
// nop
}
fin();
// clang-format on
}
} // namespace parser
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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 <cstdint>
#include <type_traits>
#include "caf/config.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace caf {
namespace detail {
namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_signed_integer(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using consumer_type = typename std::decay<Consumer>::type;
using value_type = typename consumer_type::value_type;
static_assert(std::is_integral<value_type>::value
&& std::is_signed<value_type>::value,
"expected a signed integer type");
value_type result = 0;
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
consumer.value(std::move(result));
}
});
// clang-format off
// Definition of our parser FSM.
start();
state(init) {
transition(init, " \t")
transition(has_plus, '+')
transition(has_minus, '-')
epsilon(has_plus)
}
// "+" or "-" alone aren't numbers.
state(has_plus) {
transition(pos_zero, '0')
epsilon(pos_dec, decimal_chars)
}
state(has_minus) {
transition(neg_zero, '0')
epsilon(neg_dec, decimal_chars)
}
// Disambiguate base.
term_state(pos_zero) {
transition(start_pos_bin, "bB")
transition(start_pos_hex, "xX")
epsilon(pos_oct)
}
term_state(neg_zero) {
transition(start_neg_bin, "bB")
transition(start_neg_hex, "xX")
epsilon(neg_oct)
}
// Binary integers.
state(start_pos_bin) {
epsilon(pos_bin, "01")
}
term_state(pos_bin) {
transition(pos_bin, "01", add_ascii<2>(result, ch), pec::integer_overflow)
}
state(start_neg_bin) {
epsilon(neg_bin, "01")
}
term_state(neg_bin) {
transition(neg_bin, "01", sub_ascii<2>(result, ch), pec::integer_underflow)
}
// Octal integers.
state(start_pos_oct) {
epsilon(pos_oct, octal_chars)
}
term_state(pos_oct) {
transition(pos_oct, octal_chars, add_ascii<8>(result, ch),
pec::integer_overflow)
}
state(start_neg_oct) {
epsilon(neg_oct, octal_chars)
}
term_state(neg_oct) {
transition(neg_oct, octal_chars, sub_ascii<8>(result, ch),
pec::integer_underflow)
}
// Hexal integers.
state(start_pos_hex) {
epsilon(pos_hex, hexadecimal_chars)
}
term_state(pos_hex) {
transition(pos_hex, hexadecimal_chars, add_ascii<16>(result, ch),
pec::integer_overflow)
}
state(start_neg_hex) {
epsilon(neg_hex, hexadecimal_chars)
}
term_state(neg_hex) {
transition(neg_hex, hexadecimal_chars, sub_ascii<16>(result, ch),
pec::integer_underflow)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) {
transition(pos_dec, decimal_chars, add_ascii<10>(result, ch),
pec::integer_overflow)
}
// Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) {
transition(neg_dec, decimal_chars, sub_ascii<10>(result, ch),
pec::integer_underflow)
}
fin();
// clang-format on
}
} // namespace parser
} // namespace detail
} // namespace caf
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
......@@ -38,7 +38,7 @@ namespace parser {
/// Reads a quoted or unquoted string. Quoted strings allow escaping, while
/// unquoted strings may only include alphanumeric characters.
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;
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_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. *
******************************************************************************/
#pragma once
#include <chrono>
#include <cstdint>
#include <string>
#include "caf/config.hpp"
#include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
#include "caf/timestamp.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace caf {
namespace detail {
namespace parser {
/// Reads a timespan.
template <class Iterator, class Sentinel, class Consumer>
void read_timespan(state<Iterator, Sentinel>& ps, Consumer&& consumer,
optional<int64_t> num = none) {
using namespace std::chrono;
struct interim_consumer {
using value_type = int64_t;
void value(int64_t y) {
x = y;
}
int64_t x = 0;
};
interim_consumer ic;
timespan result;
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
consumer.value(std::move(result));
});
// clang-format off
start();
state(init) {
epsilon_if(num, has_integer, any_char, ic.x = *num)
fsm_epsilon(read_signed_integer(ps, ic), has_integer)
}
state(has_integer) {
transition(have_u, 'u')
transition(have_n, 'n')
transition(have_m, 'm')
transition(done, 's', result = seconds(ic.x))
transition(done, 'h', result = hours(ic.x))
}
state(have_u) {
transition(done, 's', result = microseconds(ic.x))
}
state(have_n) {
transition(done, 's', result = nanoseconds(ic.x))
}
state(have_m) {
transition(have_mi, 'i')
transition(done, 's', result = milliseconds(ic.x))
}
state(have_mi) {
transition(done, 'n', result = minutes(ic.x))
}
term_state(done) {
// nop
}
fin();
// clang-format on
}
} // namespace parser
} // namespace detail
} // namespace caf
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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 <cstdint>
#include <type_traits>
#include "caf/config.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace caf {
namespace detail {
namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_unsigned_integer(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using consumer_type = typename std::decay<Consumer>::type;
using value_type = typename consumer_type::value_type;
static_assert(std::is_integral<value_type>::value
&& std::is_unsigned<value_type>::value,
"expected an unsigned integer type");
value_type result = 0;
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
consumer.value(std::move(result));
}
});
// clang-format off
// Definition of our parser FSM.
start();
state(init) {
transition(init, " \t")
transition(has_plus, '+')
epsilon(has_plus)
}
// "+" or "-" alone aren't numbers.
state(has_plus) {
transition(zero, '0')
epsilon(dec, decimal_chars)
}
// Disambiguate base.
term_state(zero) {
transition(start_bin, "bB")
transition(start_hex, "xX")
epsilon(oct)
}
// Binary integers.
state(start_bin) {
epsilon(bin, "01")
}
term_state(bin) {
transition(bin, "01", add_ascii<2>(result, ch), pec::integer_overflow)
}
// Octal integers.
state(start_oct) {
epsilon(oct, octal_chars)
}
term_state(oct) {
transition(oct, octal_chars, add_ascii<8>(result, ch),
pec::integer_overflow)
}
// Hexal integers.
state(start_hex) {
epsilon(hex, hexadecimal_chars)
}
term_state(hex) {
transition(hex, hexadecimal_chars, add_ascii<16>(result, ch),
pec::integer_overflow)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(dec) {
transition(dec, decimal_chars, add_ascii<10>(result, ch),
pec::integer_overflow)
}
fin();
// clang-format on
}
} // namespace parser
} // namespace detail
} // namespace caf
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
......@@ -18,6 +18,7 @@
#pragma once
#include <cctype>
#include <cstdint>
#include "caf/pec.hpp"
......@@ -34,15 +35,15 @@ struct state {
int32_t line;
int32_t column;
state() : i(), e(), code(pec::success), line(1), column(1) {
state() noexcept : i(), e(), code(pec::success), line(1), column(1) {
// nop
}
explicit state(Iterator first) : state() {
explicit state(Iterator first) noexcept : state() {
i = first;
}
state(Iterator first, Sentinel last) : state() {
state(Iterator first, Sentinel last) noexcept : state() {
i = first;
e = last;
}
......@@ -67,6 +68,28 @@ struct state {
char current() const noexcept {
return i != e ? *i : '\0';
}
/// Checks whether `i == e`.
bool at_end() const noexcept {
return i == e;
}
/// Skips any whitespaces characters in the input.
void skip_whitespaces() noexcept {
auto c = current();
while (isspace(c))
c = next();
}
/// Tries to read `x` as the next character (skips any whitespaces).
bool consume(char x) noexcept {
skip_whitespaces();
if (current() == x) {
next();
return true;
}
return false;
}
};
} // namespace parser
......
......@@ -736,6 +736,28 @@ struct is_same_ish
template <class>
struct always_false : std::false_type {};
/// Utility trait for removing const inside a `map<K, V>::value_type`.
template <class T>
struct deconst_kvp {
using type = T;
};
template <class K, class V>
struct deconst_kvp<std::pair<const K, V>> {
using type = std::pair<K, V>;
};
template <class T>
using deconst_kvp_t = typename deconst_kvp<T>::type;
/// Utility trait for checking whether T is a `std::pair`.
template <class T>
struct is_pair : std::false_type {};
/// Utility trait for checking whether T is a `std::pair`.
template <class First, class Second>
struct is_pair<std::pair<First, Second>> : std::true_type {};
// -- traits to check for STL-style type aliases -------------------------------
CAF_HAS_ALIAS_TRAIT(value_type);
......
......@@ -22,6 +22,7 @@
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_name.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
......@@ -34,34 +35,47 @@ namespace caf {
namespace detail {
template <class T>
error default_config_option_check(const config_value& x) {
error check_impl(const config_value& x) {
if (holds_alternative<T>(x))
return none;
return make_error(pec::type_mismatch);
}
template <class T>
void default_config_option_store(void* ptr, const config_value& x) {
void store_impl(void* ptr, const config_value& x) {
*static_cast<T*>(ptr) = get<T>(x);
}
template <class T>
expected<config_value> default_config_option_parse(void* ptr, string_view str) {
auto result = config_value::parse(str.begin(), str.end());
if (result) {
if (!holds_alternative<T>(*result))
return make_error(pec::type_mismatch);
if (ptr != nullptr)
*static_cast<T*>(ptr) = get<T>(*result);
config_value get_impl(const void* ptr) {
return config_value{*reinterpret_cast<const T*>(ptr)};
}
template <class T>
expected<config_value> parse_impl(T* ptr, string_view str) {
if (ptr != nullptr) {
if (auto err = parse(str, *ptr))
return err;
return config_value{*ptr};
}
return result;
T tmp;
if (auto err = parse(str, tmp))
return err;
return config_value{std::move(tmp)};
}
expected<config_value> parse_impl(std::string* ptr, string_view str);
template <class T>
expected<config_value> parse_impl_delegate(void* ptr, string_view str) {
return parse_impl(reinterpret_cast<T*>(ptr), str);
}
template <class T>
config_option::meta_state* option_meta_state_instance() {
static config_option::meta_state obj{
default_config_option_check<T>, default_config_option_store<T>, nullptr,
default_config_option_parse<T>, detail::type_name<T>()};
static config_option::meta_state obj{check_impl<T>, store_impl<T>,
get_impl<T>, parse_impl_delegate<T>,
detail::type_name<T>()};
return &obj;
}
......@@ -101,32 +115,4 @@ config_option make_ms_resolution_config_option(size_t& storage,
string_view name,
string_view description);
// -- specializations for common types -----------------------------------------
#define CAF_SPECIALIZE_META_STATE(type) \
extern config_option::meta_state type##_meta_state; \
template <> \
inline config_option::meta_state* option_meta_state_instance<type>() { \
return &type##_meta_state; \
}
namespace detail {
CAF_SPECIALIZE_META_STATE(atom_value)
CAF_SPECIALIZE_META_STATE(bool)
CAF_SPECIALIZE_META_STATE(size_t)
extern config_option::meta_state string_meta_state;
template <>
inline config_option::meta_state* option_meta_state_instance<std::string>() {
return &string_meta_state;
}
} // namespace detail
#undef CAF_SPECIALIZE_META_STATE
} // namespace caf
......@@ -18,7 +18,9 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include "caf/fwd.hpp"
......@@ -74,7 +76,7 @@ error make_error(pec code, size_t line, size_t column);
/// Returns an error object from given error code with additional context
/// information for where the parser stopped in the argument.
error make_error(pec code, std::string argument);
error make_error(pec code, string_view argument);
/// @relates pec
const char* to_string(pec x);
......
......@@ -22,6 +22,7 @@
#include <vector>
#include "caf/detail/comparable.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/unordered_flat_map.hpp"
#include "caf/fwd.hpp"
#include "caf/intrusive_ptr.hpp"
......
......@@ -32,40 +32,50 @@ using std::string;
namespace caf {
namespace {
using meta_state = config_option::meta_state;
namespace detail {
template <class T>
config_value get_impl(const void* ptr) {
CAF_ASSERT(ptr != nullptr);
return config_value{*static_cast<const T*>(ptr)};
expected<config_value> parse_impl(std::string* ptr, string_view str) {
// Parse quoted strings, otherwise consume the entire string.
auto e = str.end();
auto i = std::find_if(str.begin(), e, [](char c) { return !isspace(c); });
if (i == e) {
if (ptr != nullptr)
ptr->assign(i, e);
return config_value{std::string{i, e}};
}
if (*i == '"') {
if (ptr == nullptr) {
std::string tmp;
if (auto err = parse(str, tmp))
return err;
return config_value{std::move(tmp)};
} else {
if (auto err = parse(str, *ptr))
return err;
return config_value{*ptr};
}
}
if (ptr != nullptr)
ptr->assign(str.begin(), str.end());
return config_value{std::string{str.begin(), str.end()}};
}
error bool_check(const config_value& x) {
if (holds_alternative<bool>(x))
return none;
return make_error(pec::type_mismatch);
}
} // namespace detail
void bool_store(void* ptr, const config_value& x) {
*static_cast<bool*>(ptr) = get<bool>(x);
}
namespace {
using meta_state = config_option::meta_state;
void bool_store_neg(void* ptr, const config_value& x) {
*static_cast<bool*>(ptr) = !get<bool>(x);
}
config_value bool_get(const void* ptr) {
return config_value{*static_cast<const bool*>(ptr)};
}
config_value bool_get_neg(const void* ptr) {
return config_value{!*static_cast<const bool*>(ptr)};
}
meta_state bool_neg_meta{bool_check, bool_store_neg, bool_get_neg, nullptr,
detail::type_name<bool>()};
meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg,
nullptr, detail::type_name<bool>()};
meta_state us_res_meta{
[](const config_value& x) -> error {
......@@ -86,63 +96,25 @@ meta_state us_res_meta{
detail::type_name<timespan>()
};
meta_state ms_res_meta{
[](const config_value& x) -> error {
meta_state ms_res_meta{[](const config_value& x) -> error {
if (holds_alternative<timespan>(x))
return none;
return make_error(pec::type_mismatch);
},
[](void* ptr, const config_value& x) {
*static_cast<size_t*>(ptr) = static_cast<size_t>(get<timespan>(x).count()
/ 1000000);
*static_cast<size_t*>(ptr) = static_cast<size_t>(
get<timespan>(x).count() / 1000000);
},
[](const void* ptr) -> config_value {
auto ival = static_cast<int64_t>(*static_cast<const size_t*>(ptr));
auto ival = static_cast<int64_t>(
*static_cast<const size_t*>(ptr));
timespan val{ival * 1000000};
return config_value{val};
},
nullptr,
detail::type_name<timespan>()};
expected<config_value> parse_atom(void* ptr, string_view str) {
if (str.size() > 10)
return make_error(pec::too_many_characters);
auto is_illegal = [](char c) {
return !(isalnum(c) || c == '_' || c == ' ');
};
auto i = std::find_if(str.begin(), str.end(), is_illegal);
if (i != str.end())
return make_error(pec::unexpected_character,
std::string{"invalid character: "} + *i);
auto result = atom_from_string(str);
if (ptr != nullptr)
*reinterpret_cast<atom_value*>(ptr) = result;
return config_value{result};
}
expected<config_value> parse_string(void* ptr, string_view str) {
std::string result{str.begin(), str.end()};
if (ptr != nullptr)
*reinterpret_cast<std::string*>(ptr) = result;
return config_value{std::move(result)};
}
nullptr, detail::type_name<timespan>()};
} // namespace
namespace detail {
DEFAULT_META(atom_value, parse_atom)
DEFAULT_META(size_t, default_config_option_parse<size_t>)
DEFAULT_META(string, parse_string)
config_option::meta_state bool_meta_state{bool_check, bool_store, bool_get,
default_config_option_parse<bool>,
detail::type_name<bool>()};
} // namespace detail
config_option make_negated_config_option(bool& storage, string_view category,
string_view name,
string_view description) {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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/parse.hpp"
#include "caf/detail/consumer.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/read_atom.hpp"
#include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/detail/parser/read_timespan.hpp"
#include "caf/detail/parser/read_unsigned_integer.hpp"
#include "caf/detail/parser/read_uri.hpp"
#include "caf/uri_builder.hpp"
#define PARSE_IMPL(type, parser_name) \
void parse(parse_state& ps, type& x) { \
parser::read_##parser_name(ps, make_consumer(x)); \
}
namespace caf {
namespace detail {
PARSE_IMPL(bool, bool)
PARSE_IMPL(int8_t, signed_integer)
PARSE_IMPL(int16_t, signed_integer)
PARSE_IMPL(int32_t, signed_integer)
PARSE_IMPL(int64_t, signed_integer)
PARSE_IMPL(uint8_t, unsigned_integer)
PARSE_IMPL(uint16_t, unsigned_integer)
PARSE_IMPL(uint32_t, unsigned_integer)
PARSE_IMPL(uint64_t, unsigned_integer)
PARSE_IMPL(float, floating_point)
PARSE_IMPL(double, floating_point)
PARSE_IMPL(timespan, timespan)
void parse(parse_state& ps, atom_value& x) {
parser::read_atom(ps, make_consumer(x), true);
}
void parse(parse_state& ps, uri& x) {
uri_builder builder;
if (ps.consume('<')) {
parser::read_uri(ps, builder);
if (ps.code > pec::trailing_character)
return;
if (!ps.consume('>')) {
ps.code = pec::unexpected_character;
return;
}
} else {
read_uri(ps, builder);
}
if (ps.code <= pec::trailing_character)
x = builder.make();
}
void parse(parse_state& ps, std::string& x) {
ps.skip_whitespaces();
if (ps.current() == '"') {
parser::read_string(ps, make_consumer(x));
return;
}
auto c = ps.current();
while (c != '\0' && (isalnum(c) || isspace(c))) {
x += c;
c = ps.next();
}
while (!x.empty() && isspace(x.back()))
x.pop_back();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
} // namespace detail
} // namespace caf
......@@ -21,6 +21,7 @@
#include "caf/config_value.hpp"
#include "caf/error.hpp"
#include "caf/make_message.hpp"
#include "caf/string_view.hpp"
namespace {
......@@ -61,9 +62,9 @@ error make_error(pec code, size_t line, size_t column) {
make_message(std::move(context))};
}
error make_error(pec code, std::string argument) {
error make_error(pec code, string_view argument) {
config_value::dictionary context;
context["argument"] = std::move(argument);
context["argument"] = std::string{argument.begin(), argument.end()};
return {static_cast<uint8_t>(code), atom("parser"),
make_message(std::move(context))};
}
......
......@@ -22,12 +22,12 @@
#include "caf/detail/append_percent_encoded.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/detail/overload.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/parser/read_uri.hpp"
#include "caf/detail/uri_impl.hpp"
#include "caf/error.hpp"
#include "caf/make_counted.hpp"
#include "caf/serializer.hpp"
#include "caf/uri_builder.hpp"
namespace caf {
......@@ -131,16 +131,12 @@ std::string to_string(const uri::authority_type& x) {
}
error parse(string_view str, uri& dest) {
using namespace detail::parser;
uri_builder builder;
state<string_view::iterator> res{str.begin(), str.end()};
read_uri(res, builder);
if (res.code == pec::success) {
dest = builder.make();
detail::parse_state ps{str.begin(), str.end()};
parse(ps, dest);
if (ps.code == pec::success)
return none;
}
return make_error(res.code, static_cast<size_t>(res.line),
static_cast<size_t>(res.column));
return make_error(ps.code, static_cast<size_t>(ps.line),
static_cast<size_t>(ps.column));
}
} // namespace caf
......@@ -49,7 +49,8 @@ optional<T> read(string_view arg) {
auto co = make_config_option<T>(category, name, explanation);
auto res = co.parse(arg);
if (res && holds_alternative<T>(*res)) {
CAF_CHECK_EQUAL(co.check(*res), none);
if (co.check(*res) != none)
CAF_ERROR("co.parse() produced the wrong type!");
return get<T>(*res);
}
return none;
......@@ -155,20 +156,20 @@ CAF_TEST(type int64_t) {
CAF_TEST(type float) {
CAF_CHECK_EQUAL(unbox(read<float>("-1.0")), -1.0f);
CAF_CHECK_EQUAL(unbox(read<float>("-0.1")), -0.1f);
CAF_CHECK_EQUAL(read<float>("0"), none);
CAF_CHECK_EQUAL(read<float>("0"), 0.f);
CAF_CHECK_EQUAL(read<float>("\"0.1\""), none);
}
CAF_TEST(type double) {
CAF_CHECK_EQUAL(unbox(read<double>("-1.0")), -1.0);
CAF_CHECK_EQUAL(unbox(read<double>("-0.1")), -0.1);
CAF_CHECK_EQUAL(read<double>("0"), none);
CAF_CHECK_EQUAL(read<double>("0"), 0.);
CAF_CHECK_EQUAL(read<double>("\"0.1\""), none);
}
CAF_TEST(type string) {
CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "\"foo\"");
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "foo");
}
CAF_TEST(type atom) {
......@@ -182,6 +183,14 @@ CAF_TEST(type timespan) {
CAF_CHECK_EQUAL(unbox(read<timespan>("500ns")), dur);
}
CAF_TEST(lists) {
using int_list = std::vector<int>;
CAF_CHECK_EQUAL(read<int_list>(""), int_list({}));
CAF_CHECK_EQUAL(read<int_list>("[]"), int_list({}));
CAF_CHECK_EQUAL(read<int_list>("1, 2, 3"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>("[1, 2, 3]"), int_list({1, 2, 3}));
}
CAF_TEST(flat CLI parsing) {
auto x = make_config_option<std::string>("?foo", "bar,b", "test option");
CAF_CHECK_EQUAL(x.category(), "foo");
......
......@@ -124,20 +124,20 @@ CAF_TEST(atom parameters) {
CAF_TEST(string parameters) {
opts.add<std::string>("value,v", "some value");
CAF_MESSAGE("test string option with and without quotes");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"foobar\""}), "\"foobar\"");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"foo\\tbar\""}), "foo\tbar");
CAF_CHECK_EQUAL(read<std::string>({"--value=foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"foobar\""}), "\"foobar\"");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"foobar\""}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v", "foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v\"foobar\""}), "\"foobar\"");
CAF_CHECK_EQUAL(read<std::string>({"-v\"foobar\""}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-vfoobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"'abc'\""}), "\"'abc'\"");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"'abc'\""}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"--value='abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"'abc'\""}), "\"'abc'\"");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"'abc'\""}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v", "'abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v'abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"123\""}), "\"123\"");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"123\""}), "123");
CAF_CHECK_EQUAL(read<std::string>({"--value=123"}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"123\""}), "\"123\"");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"123\""}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v", "123"}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v123"}), "123");
}
......
......@@ -132,6 +132,8 @@ CAF_TEST(timespan) {
CAF_TEST(list) {
using integer_list = std::vector<int64_t>;
auto xs = make_config_value_list(1, 2, 3);
auto ys = config_value{integer_list{1, 2, 3}};
CAF_CHECK_EQUAL(xs, ys);
CAF_CHECK_EQUAL(to_string(xs), "[1, 2, 3]");
CAF_CHECK_EQUAL(xs.type_name(), "list"_s);
CAF_CHECK_EQUAL(holds_alternative<config_value::list>(xs), true);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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. *
******************************************************************************/
#define CAF_SUITE parse
#include "caf/detail/parse.hpp"
#include "caf/test/dsl.hpp"
#include "caf/expected.hpp"
#include "caf/string_view.hpp"
#include "caf/uri.hpp"
using namespace caf;
namespace {
using std::chrono::duration_cast;
timespan operator"" _ns(unsigned long long x) {
return duration_cast<timespan>(std::chrono::nanoseconds(x));
}
timespan operator"" _us(unsigned long long x) {
return duration_cast<timespan>(std::chrono::microseconds(x));
}
timespan operator"" _ms(unsigned long long x) {
return duration_cast<timespan>(std::chrono::milliseconds(x));
}
timespan operator"" _s(unsigned long long x) {
return duration_cast<timespan>(std::chrono::seconds(x));
}
timespan operator"" _h(unsigned long long x) {
return duration_cast<timespan>(std::chrono::hours(x));
}
template <class T>
expected<T> read(string_view str) {
T result;
detail::parse_state ps{str.begin(), str.end()};
detail::parse(ps, result);
if (ps.code == pec::success)
return result;
return make_error(ps.code, ps.line, ps.column);
}
} // namespace
#define CHECK_NUMBER(type, value) \
CAF_CHECK_EQUAL(read<type>(#value), type(value))
#define CHECK_NUMBER_3(type, value, cpp_value) \
CAF_CHECK_EQUAL(read<type>(#value), type(cpp_value))
#define CHECK_INVALID(type, str, code) CAF_CHECK_EQUAL(read<type>(str), code)
CAF_TEST(valid signed integers) {
CHECK_NUMBER(int8_t, -128);
CHECK_NUMBER(int8_t, 127);
CHECK_NUMBER(int8_t, +127);
CHECK_NUMBER(int16_t, -32768);
CHECK_NUMBER(int16_t, 32767);
CHECK_NUMBER(int16_t, +32767);
CHECK_NUMBER(int32_t, -2147483648);
CHECK_NUMBER(int32_t, 2147483647);
CHECK_NUMBER(int32_t, +2147483647);
CHECK_NUMBER(int64_t, -9223372036854775807);
CHECK_NUMBER(int64_t, 9223372036854775807);
CHECK_NUMBER(int64_t, +9223372036854775807);
}
CAF_TEST(invalid signed integers) {
CHECK_INVALID(int8_t, "--1", pec::unexpected_character);
CHECK_INVALID(int8_t, "++1", pec::unexpected_character);
CHECK_INVALID(int8_t, "-129", pec::integer_underflow);
CHECK_INVALID(int8_t, "128", pec::integer_overflow);
CHECK_INVALID(int8_t, "~1", pec::unexpected_character);
CHECK_INVALID(int8_t, "1!", pec::trailing_character);
CHECK_INVALID(int8_t, "+", pec::unexpected_eof);
CHECK_INVALID(int8_t, "-", pec::unexpected_eof);
}
CAF_TEST(valid unsigned integers) {
CHECK_NUMBER(uint8_t, 0);
CHECK_NUMBER(uint8_t, +0);
CHECK_NUMBER(uint8_t, 255);
CHECK_NUMBER(uint8_t, +255);
CHECK_NUMBER(uint16_t, 0);
CHECK_NUMBER(uint16_t, +0);
CHECK_NUMBER(uint16_t, 65535);
CHECK_NUMBER(uint16_t, +65535);
CHECK_NUMBER(uint32_t, 0);
CHECK_NUMBER(uint32_t, +0);
CHECK_NUMBER(uint32_t, 4294967295);
CHECK_NUMBER(uint32_t, +4294967295);
CHECK_NUMBER(uint64_t, 0);
CHECK_NUMBER(uint64_t, +0);
CHECK_NUMBER_3(uint64_t, 18446744073709551615, 18446744073709551615ULL);
CHECK_NUMBER_3(uint64_t, +18446744073709551615, 18446744073709551615ULL);
}
CAF_TEST(invalid unsigned integers) {
CHECK_INVALID(uint8_t, "-1", pec::unexpected_character);
CHECK_INVALID(uint8_t, "++1", pec::unexpected_character);
CHECK_INVALID(uint8_t, "256", pec::integer_overflow);
CHECK_INVALID(uint8_t, "~1", pec::unexpected_character);
CHECK_INVALID(uint8_t, "1!", pec::trailing_character);
CHECK_INVALID(uint8_t, "+", pec::unexpected_eof);
}
CAF_TEST(valid floating point numbers) {
CHECK_NUMBER(float, 1);
CHECK_NUMBER(double, 1);
CHECK_NUMBER(double, 0.01e10);
CHECK_NUMBER(double, 10e-10);
CHECK_NUMBER(double, -10e-10);
}
CAF_TEST(invalid floating point numbers) {
CHECK_INVALID(float, "1..", pec::trailing_character);
CHECK_INVALID(double, "..1", pec::unexpected_character);
CHECK_INVALID(double, "+", pec::unexpected_eof);
CHECK_INVALID(double, "-", pec::unexpected_eof);
CHECK_INVALID(double, "1e", pec::unexpected_eof);
CHECK_INVALID(double, "--0.01e10", pec::unexpected_character);
CHECK_INVALID(double, "++10e-10", pec::unexpected_character);
}
CAF_TEST(valid timespans) {
CAF_CHECK_EQUAL(read<timespan>("12ns"), 12_ns);
CAF_CHECK_EQUAL(read<timespan>("34us"), 34_us);
CAF_CHECK_EQUAL(read<timespan>("56ms"), 56_ms);
CAF_CHECK_EQUAL(read<timespan>("78s"), 78_s);
CAF_CHECK_EQUAL(read<timespan>("60min"), 1_h);
CAF_CHECK_EQUAL(read<timespan>("90h"), 90_h);
}
CAF_TEST(invalid timespans) {
CAF_CHECK_EQUAL(read<timespan>("12"), pec::unexpected_eof);
CAF_CHECK_EQUAL(read<timespan>("12nas"), pec::unexpected_character);
CAF_CHECK_EQUAL(read<timespan>("34usec"), pec::trailing_character);
CAF_CHECK_EQUAL(read<timespan>("56m"), pec::unexpected_eof);
}
CAF_TEST(valid atom values) {
CAF_CHECK_EQUAL(read<atom_value>("foo"), atom("foo"));
CAF_CHECK_EQUAL(read<atom_value>("'foo'"), atom("foo"));
CAF_CHECK_EQUAL(read<atom_value>("foooooooooo"), pec::too_many_characters);
CAF_CHECK_EQUAL(read<atom_value>("foo,bar"), pec::trailing_character);
CAF_CHECK_EQUAL(read<atom_value>("$"), pec::unexpected_character);
}
CAF_TEST(strings) {
CAF_CHECK_EQUAL(read<std::string>(" foo\t "), "foo");
CAF_CHECK_EQUAL(read<std::string>(" \" foo\t\" "), " foo\t");
}
CAF_TEST(lists) {
using int_list = std::vector<int>;
using atom_list = std::vector<atom_value>;
using string_list = std::vector<std::string>;
CAF_CHECK_EQUAL(read<int_list>("1"), int_list({1}));
CAF_CHECK_EQUAL(read<int_list>("1, 2, 3"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>("[1, 2, 3]"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<atom_list>("foo, bar, baz"),
atom_list({atom("foo"), atom("bar"), atom("baz")}));
CAF_CHECK_EQUAL(read<atom_list>("'foo', bar, 'baz'"),
atom_list({atom("foo"), atom("bar"), atom("baz")}));
CAF_CHECK_EQUAL(read<atom_list>("[ foo , 'bar', 'baz'] "),
atom_list({atom("foo"), atom("bar"), atom("baz")}));
CAF_CHECK_EQUAL(read<string_list>("a, b , \" c \""),
string_list({"a", "b", " c "}));
}
CAF_TEST(maps) {
using int_map = std::map<atom_value, int>;
CAF_CHECK_EQUAL(read<int_map>("a=1, 'b' = 42"),
int_map({{atom("a"), 1}, {atom("b"), 42}}));
CAF_CHECK_EQUAL(read<int_map>("{ a = 1 , 'b' = 42 ,} \t "),
int_map({{atom("a"), 1}, {atom("b"), 42}}));
}
CAF_TEST(uris) {
using uri_list = std::vector<uri>;
auto x_res = read<uri>("foo:bar");
if (x_res == none) {
CAF_ERROR("my:path not recognized as URI");
return;
}
auto x = *x_res;
CAF_CHECK_EQUAL(x.scheme(), "foo");
CAF_CHECK_EQUAL(x.path(), "bar");
auto ls = unbox(read<uri_list>("foo:bar, <http://actor-framework.org/doc>"));
CAF_REQUIRE_EQUAL(ls.size(), 2u);
CAF_CHECK_EQUAL(ls[0].scheme(), "foo");
CAF_CHECK_EQUAL(ls[0].path(), "bar");
CAF_CHECK_EQUAL(ls[1].scheme(), "http");
CAF_CHECK_EQUAL(ls[1].authority().host, std::string{"actor-framework.org"});
CAF_CHECK_EQUAL(ls[1].path(), "doc");
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#define CAF_SUITE read_floating_point
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/test/dsl.hpp"
#include <string>
#include "caf/detail/parser/state.hpp"
#include "caf/pec.hpp"
using namespace caf;
namespace {
struct double_consumer {
using value_type = double;
void value(double y) {
x = y;
}
double x;
};
optional<double> read(string_view str) {
double_consumer consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_floating_point(ps, consumer);
if (ps.code != pec::success)
return none;
return consumer.x;
}
} // namespace
CAF_TEST(predecimal only) {
CAF_CHECK_EQUAL(read("0"), 0.);
CAF_CHECK_EQUAL(read("+0"), 0.);
CAF_CHECK_EQUAL(read("-0"), 0.);
CAF_CHECK_EQUAL(read("1"), 1.);
CAF_CHECK_EQUAL(read("+1"), 1.);
CAF_CHECK_EQUAL(read("-1"), -1.);
CAF_CHECK_EQUAL(read("12"), 12.);
CAF_CHECK_EQUAL(read("+12"), 12.);
CAF_CHECK_EQUAL(read("-12"), -12.);
}
CAF_TEST(trailing dot) {
CAF_CHECK_EQUAL(read("0."), 0.);
CAF_CHECK_EQUAL(read("1."), 1.);
CAF_CHECK_EQUAL(read("+1."), 1.);
CAF_CHECK_EQUAL(read("-1."), -1.);
CAF_CHECK_EQUAL(read("12."), 12.);
CAF_CHECK_EQUAL(read("+12."), 12.);
CAF_CHECK_EQUAL(read("-12."), -12.);
}
CAF_TEST(leading dot) {
CAF_CHECK_EQUAL(read(".0"), .0);
CAF_CHECK_EQUAL(read(".1"), .1);
CAF_CHECK_EQUAL(read("+.1"), .1);
CAF_CHECK_EQUAL(read("-.1"), -.1);
CAF_CHECK_EQUAL(read(".12"), .12);
CAF_CHECK_EQUAL(read("+.12"), .12);
CAF_CHECK_EQUAL(read("-.12"), -.12);
}
CAF_TEST(regular noation) {
CAF_CHECK_EQUAL(read("0.0"), .0);
CAF_CHECK_EQUAL(read("1.2"), 1.2);
CAF_CHECK_EQUAL(read("1.23"), 1.23);
CAF_CHECK_EQUAL(read("12.34"), 12.34);
}
CAF_TEST(scientific noation) {
CAF_CHECK_EQUAL(read("1e2"), 1e2);
CAF_CHECK_EQUAL(read("+1e2"), 1e2);
CAF_CHECK_EQUAL(read("+1e+2"), 1e2);
CAF_CHECK_EQUAL(read("-1e2"), -1e2);
CAF_CHECK_EQUAL(read("-1e+2"), -1e2);
CAF_CHECK_EQUAL(read("12e-3"), 12e-3);
CAF_CHECK_EQUAL(read("+12e-3"), 12e-3);
CAF_CHECK_EQUAL(read("-12e-3"), -12e-3);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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. *
******************************************************************************/
#define CAF_SUITE read_signed_integer
#include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/test/dsl.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/string_view.hpp"
using namespace caf;
namespace {
template <class T>
struct signed_integer_consumer {
using value_type = T;
void value(T y) {
x = y;
}
T x;
};
template <class T>
optional<T> read(string_view str) {
signed_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_signed_integer(ps, consumer);
if (ps.code != pec::success)
return none;
return consumer.x;
}
template <class T>
bool underflow(string_view str) {
signed_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_signed_integer(ps, consumer);
return ps.code == pec::integer_underflow;
}
template <class T>
bool overflow(string_view str) {
signed_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_signed_integer(ps, consumer);
return ps.code == pec::integer_overflow;
}
template <class T>
T min_val() {
return std::numeric_limits<T>::min();
}
template <class T>
T max_val() {
return std::numeric_limits<T>::max();
}
} // namespace
#define ZERO_VALUE(type, literal) \
CAF_CHECK_EQUAL(read<type>(#literal), type(0));
#define MAX_VALUE(type, literal) \
CAF_CHECK_EQUAL(read<type>(#literal), max_val<type>());
#define MIN_VALUE(type, literal) \
CAF_CHECK_EQUAL(read<type>(#literal), min_val<type>());
#ifdef OVERFLOW
# undef OVERFLOW
#endif // OVERFLOW
#define OVERFLOW(type, literal) CAF_CHECK(overflow<type>(#literal));
#ifdef UNDERFLOW
# undef UNDERFLOW
#endif // UNDERFLOW
#define UNDERFLOW(type, literal) CAF_CHECK(underflow<type>(#literal));
CAF_TEST(read zeros) {
ZERO_VALUE(int8_t, 0);
ZERO_VALUE(int8_t, 00);
ZERO_VALUE(int8_t, 0x0);
ZERO_VALUE(int8_t, 0X00);
ZERO_VALUE(int8_t, 0b0);
ZERO_VALUE(int8_t, 0B00);
ZERO_VALUE(int8_t, +0);
ZERO_VALUE(int8_t, +00);
ZERO_VALUE(int8_t, +0x0);
ZERO_VALUE(int8_t, +0X00);
ZERO_VALUE(int8_t, +0b0);
ZERO_VALUE(int8_t, +0B00);
ZERO_VALUE(int8_t, -0);
ZERO_VALUE(int8_t, -00);
ZERO_VALUE(int8_t, -0x0);
ZERO_VALUE(int8_t, -0X00);
ZERO_VALUE(int8_t, -0b0);
ZERO_VALUE(int8_t, -0B00);
}
CAF_TEST(minimal value) {
MIN_VALUE(int8_t, -0b10000000);
MIN_VALUE(int8_t, -0200);
MIN_VALUE(int8_t, -128);
MIN_VALUE(int8_t, -0x80);
UNDERFLOW(int8_t, -0b10000001);
UNDERFLOW(int8_t, -0201);
UNDERFLOW(int8_t, -129);
UNDERFLOW(int8_t, -0x81);
MIN_VALUE(int16_t, -0b1000000000000000);
MIN_VALUE(int16_t, -0100000);
MIN_VALUE(int16_t, -32768);
MIN_VALUE(int16_t, -0x8000);
UNDERFLOW(int16_t, -0b1000000000000001);
UNDERFLOW(int16_t, -0100001);
UNDERFLOW(int16_t, -32769);
UNDERFLOW(int16_t, -0x8001);
MIN_VALUE(int32_t, -0b10000000000000000000000000000000);
MIN_VALUE(int32_t, -020000000000);
MIN_VALUE(int32_t, -2147483648);
MIN_VALUE(int32_t, -0x80000000);
UNDERFLOW(int32_t, -0b10000000000000000000000000000001);
UNDERFLOW(int32_t, -020000000001);
UNDERFLOW(int32_t, -2147483649);
UNDERFLOW(int32_t, -0x80000001);
MIN_VALUE(int64_t, -01000000000000000000000);
MIN_VALUE(int64_t, -9223372036854775808);
MIN_VALUE(int64_t, -0x8000000000000000);
UNDERFLOW(int64_t, -01000000000000000000001);
UNDERFLOW(int64_t, -9223372036854775809);
UNDERFLOW(int64_t, -0x8000000000000001);
}
CAF_TEST(maximal value) {
MAX_VALUE(int8_t, 0b1111111);
MAX_VALUE(int8_t, 0177);
MAX_VALUE(int8_t, 127);
MAX_VALUE(int8_t, 0x7F);
OVERFLOW(int8_t, 0b10000000);
OVERFLOW(int8_t, 0200);
OVERFLOW(int8_t, 128);
OVERFLOW(int8_t, 0x80);
MAX_VALUE(int16_t, 0b111111111111111);
MAX_VALUE(int16_t, 077777);
MAX_VALUE(int16_t, 32767);
MAX_VALUE(int16_t, 0x7FFF);
OVERFLOW(int16_t, 0b1000000000000000);
OVERFLOW(int16_t, 0100000);
OVERFLOW(int16_t, 32768);
OVERFLOW(int16_t, 0x8000);
MAX_VALUE(int32_t, 0b1111111111111111111111111111111);
MAX_VALUE(int32_t, 017777777777);
MAX_VALUE(int32_t, 2147483647);
MAX_VALUE(int32_t, 0x7FFFFFFF);
OVERFLOW(int32_t, 0b10000000000000000000000000000000);
OVERFLOW(int32_t, 020000000000);
OVERFLOW(int32_t, 2147483648);
OVERFLOW(int32_t, 0x80000000);
MAX_VALUE(int64_t,
0b111111111111111111111111111111111111111111111111111111111111111);
MAX_VALUE(int64_t, 0777777777777777777777);
MAX_VALUE(int64_t, 9223372036854775807);
MAX_VALUE(int64_t, 0x7FFFFFFFFFFFFFFF);
OVERFLOW(int64_t,
0b1000000000000000000000000000000000000000000000000000000000000000);
OVERFLOW(int64_t, 01000000000000000000000);
OVERFLOW(int64_t, 9223372036854775808);
OVERFLOW(int64_t, 0x8000000000000000);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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. *
******************************************************************************/
#define CAF_SUITE read_timespan
#include "caf/detail/parser/read_timespan.hpp"
#include "caf/test/dsl.hpp"
#include <chrono>
using namespace caf;
namespace {
using std::chrono::duration_cast;
timespan operator"" _ns(unsigned long long x) {
return duration_cast<timespan>(std::chrono::nanoseconds(x));
}
timespan operator"" _us(unsigned long long x) {
return duration_cast<timespan>(std::chrono::microseconds(x));
}
timespan operator"" _ms(unsigned long long x) {
return duration_cast<timespan>(std::chrono::milliseconds(x));
}
timespan operator"" _s(unsigned long long x) {
return duration_cast<timespan>(std::chrono::seconds(x));
}
timespan operator"" _h(unsigned long long x) {
return duration_cast<timespan>(std::chrono::hours(x));
}
struct timespan_consumer {
using value_type = timespan;
void value(timespan y) {
x = y;
}
timespan x;
};
optional<timespan> read(string_view str) {
timespan_consumer consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_timespan(ps, consumer);
if (ps.code != pec::success)
return none;
return consumer.x;
}
} // namespace
CAF_TEST(todo) {
CAF_CHECK_EQUAL(read("12ns"), 12_ns);
CAF_CHECK_EQUAL(read("34us"), 34_us);
CAF_CHECK_EQUAL(read("56ms"), 56_ms);
CAF_CHECK_EQUAL(read("78s"), 78_s);
CAF_CHECK_EQUAL(read("60min"), 1_h);
CAF_CHECK_EQUAL(read("90h"), 90_h);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 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. *
******************************************************************************/
#define CAF_SUITE read_unsigned_integer
#include "caf/detail/parser/read_unsigned_integer.hpp"
#include "caf/test/dsl.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/string_view.hpp"
using namespace caf;
namespace {
template <class T>
struct unsigned_integer_consumer {
using value_type = T;
void value(T y) {
x = y;
}
T x;
};
template <class T>
optional<T> read(string_view str) {
unsigned_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_unsigned_integer(ps, consumer);
if (ps.code != pec::success)
return none;
return consumer.x;
}
template <class T>
bool overflow(string_view str) {
unsigned_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
detail::parser::read_unsigned_integer(ps, consumer);
return ps.code == pec::integer_overflow;
}
template <class T>
T max_val() {
return std::numeric_limits<T>::max();
}
} // namespace
#define ZERO_VALUE(type, literal) \
CAF_CHECK_EQUAL(read<type>(#literal), type(0));
#define MAX_VALUE(type, literal) \
CAF_CHECK_EQUAL(read<type>(#literal), max_val<type>());
#ifdef OVERFLOW
# undef OVERFLOW
#endif // OVERFLOW
#define OVERFLOW(type, literal) CAF_CHECK(overflow<type>(#literal));
CAF_TEST(read zeros) {
ZERO_VALUE(uint8_t, 0);
ZERO_VALUE(uint8_t, 00);
ZERO_VALUE(uint8_t, 0x0);
ZERO_VALUE(uint8_t, 0X00);
ZERO_VALUE(uint8_t, 0b0);
ZERO_VALUE(uint8_t, 0B00);
ZERO_VALUE(uint8_t, +0);
ZERO_VALUE(uint8_t, +00);
ZERO_VALUE(uint8_t, +0x0);
ZERO_VALUE(uint8_t, +0X00);
ZERO_VALUE(uint8_t, +0b0);
ZERO_VALUE(uint8_t, +0B00);
}
CAF_TEST(maximal value) {
MAX_VALUE(uint8_t, 0b11111111);
MAX_VALUE(uint8_t, 0377);
MAX_VALUE(uint8_t, 255);
MAX_VALUE(uint8_t, 0xFF);
OVERFLOW(uint8_t, 0b100000000);
OVERFLOW(uint8_t, 0400);
OVERFLOW(uint8_t, 256);
OVERFLOW(uint8_t, 0x100);
MAX_VALUE(uint16_t, 0b1111111111111111);
MAX_VALUE(uint16_t, 0177777);
MAX_VALUE(uint16_t, 65535);
MAX_VALUE(uint16_t, 0xFFFF);
OVERFLOW(uint16_t, 0b10000000000000000);
OVERFLOW(uint16_t, 0200000);
OVERFLOW(uint16_t, 65536);
OVERFLOW(uint16_t, 0x10000);
MAX_VALUE(uint32_t, 0b11111111111111111111111111111111);
MAX_VALUE(uint32_t, 037777777777);
MAX_VALUE(uint32_t, 4294967295);
MAX_VALUE(uint32_t, 0xFFFFFFFF);
OVERFLOW(uint32_t, 0b100000000000000000000000000000000);
OVERFLOW(uint32_t, 040000000000);
OVERFLOW(uint32_t, 4294967296);
OVERFLOW(uint32_t, 0x100000000);
MAX_VALUE(uint64_t,
0b1111111111111111111111111111111111111111111111111111111111111111);
MAX_VALUE(uint64_t, 01777777777777777777777);
MAX_VALUE(uint64_t, 18446744073709551615);
MAX_VALUE(uint64_t, 0xFFFFFFFFFFFFFFFF);
OVERFLOW(uint64_t,
0b10000000000000000000000000000000000000000000000000000000000000000);
OVERFLOW(uint64_t, 02000000000000000000000);
OVERFLOW(uint64_t, 18446744073709551616);
OVERFLOW(uint64_t, 0x10000000000000000);
}
......@@ -162,18 +162,34 @@ class caf_handle : caf::detail::comparable<caf_handle>,
public:
using pointer = caf::abstract_actor*;
constexpr caf_handle() : ptr_(nullptr) {
constexpr caf_handle(pointer ptr = nullptr) : ptr_(ptr) {
// nop
}
template <class T>
caf_handle(const T& x) {
*this = x;
caf_handle(const caf::strong_actor_ptr& x) {
set(x);
}
caf_handle(const caf::actor& x) {
set(x);
}
caf_handle(const caf::actor_addr& x) {
set(x);
}
caf_handle(const caf::scoped_actor& x) {
set(x);
}
template <class... Ts>
caf_handle(const caf::typed_actor<Ts...>& x) {
set(x);
}
caf_handle(const caf_handle&) = default;
inline caf_handle& operator=(caf::abstract_actor* x) {
caf_handle& operator=(pointer x) {
ptr_ = x;
return *this;
}
......@@ -181,7 +197,7 @@ public:
template <class T,
class E = caf::detail::enable_if_t<!std::is_pointer<T>::value>>
caf_handle& operator=(const T& x) {
ptr_ = caf::actor_cast<pointer>(x);
set(x);
return *this;
}
......@@ -205,6 +221,11 @@ public:
}
private:
template <class T>
void set(const T& x) {
ptr_ = caf::actor_cast<pointer>(x);
}
caf::abstract_actor* ptr_;
};
......
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