Commit d4cf1f7a authored by Dominik Charousset's avatar Dominik Charousset

Simplifiy DSL for FSM declarations

parent 115b4fd0
......@@ -52,6 +52,7 @@ set(LIBCAF_CORE_SRCS
src/execution_unit.cpp
src/exit_reason.cpp
src/forwarding_actor_proxy.cpp
src/fsm.cpp
src/get_mac_addresses.cpp
src/get_process_id.cpp
src/get_root_uuid.cpp
......
......@@ -18,6 +18,40 @@
#pragma once
#include <cstring>
namespace caf {
namespace detail {
struct any_char_t {};
constexpr any_char_t any_char = any_char_t{};
inline constexpr bool in_whitelist(any_char_t, char) {
return true;
}
inline constexpr bool in_whitelist(char whitelist, char ch) {
return whitelist == ch;
}
inline bool in_whitelist(const char* whitelist, char ch) {
return strchr(whitelist, ch) != nullptr;
}
inline bool in_whitelist(bool (*filter)(char), char ch) {
return filter(ch);
}
extern const char alphanumeric_chars[63];
extern const char alphabetic_chars[53];
extern const char hexadecimal_chars[23];
extern const char decimal_chars[11];
extern const char octal_chars[9];
} // namespace detail
} // namespace caf
#define CAF_FSM_EVAL_MISMATCH_EC \
if (mismatch_ec == ec::unexpected_character) \
ps.code = ch != '\n' ? mismatch_ec : ec::unexpected_newline; \
......@@ -61,72 +95,115 @@
ps.code = ec::success; \
return;
#define input(predicate, target) \
if (predicate(ch)) { \
ch = ps.next(); \
goto s_##target; \
}
#define CAF_TRANSITION_IMPL1(target) \
ch = ps.next(); \
goto s_##target;
#define action(predicate, target, statement) \
if (predicate(ch)) { \
statement; \
ch = ps.next(); \
goto s_##target; \
#define CAF_TRANSITION_IMPL2(target, whitelist) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
CAF_TRANSITION_IMPL1(target) \
}
#define default_action(target, statement) \
statement; \
ch = ps.next(); \
goto s_##target;
#define CAF_TRANSITION_IMPL3(target, whitelist, action) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
action; \
CAF_TRANSITION_IMPL1(target) \
}
#define checked_action(predicate, target, statement, error_code) \
if (predicate(ch)) { \
if (statement) { \
ch = ps.next(); \
goto s_##target; \
} else { \
#define CAF_TRANSITION_IMPL4(target, whitelist, action, error_code) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
if (!action) { \
ps.code = error_code; \
return; \
} \
CAF_TRANSITION_IMPL1(target) \
}
// Unconditionally jumps to target for any input.
#define any_input(target) \
ch = ps.next(); \
goto s_##target;
/// Transitions to target state if a predicate (optional argument 1) holds for
/// the current token and executes an action (optional argument 2) before
/// entering the new state.
#define transition(...) \
CAF_PP_OVERLOAD(CAF_TRANSITION_IMPL, __VA_ARGS__)(__VA_ARGS__)
#define invalid_input(predicate, error_code) \
if (predicate(ch)) { \
#define CAF_ERROR_TRANSITION_IMPL2(error_code, whitelist) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
ps.code = error_code; \
return; \
}
#define default_failure(error_code) \
#define CAF_ERROR_TRANSITION_IMPL1(error_code) \
ps.code = error_code; \
return;
// Makes an unconditional epsiolon transition into another state.
#define epsilon(target) goto e_##target;
/// Stops the FSM with reason `error_code` if `predicate` holds for the current
/// token.
#define error_transition(...) \
CAF_PP_OVERLOAD(CAF_ERROR_TRANSITION_IMPL, __VA_ARGS__)(__VA_ARGS__)
#define CAF_EPSILON_IMPL1(target) goto s_##target;
#define CAF_EPSILON_IMPL2(target, whitelist) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
CAF_EPSILON_IMPL1(target) \
}
#define CAF_EPSILON_IMPL3(target, whitelist, action) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
action; \
CAF_EPSILON_IMPL1(target) \
}
#define CAF_EPSILON_IMPL4(target, whitelist, action, error_code) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
if (!action) { \
ps.code = error_code; \
return; \
} \
CAF_EPSILON_IMPL1(target) \
}
// Makes an epsilon transition into another state.
#define epsilon(...) CAF_PP_OVERLOAD(CAF_EPSILON_IMPL, __VA_ARGS__)(__VA_ARGS__)
// Makes an epsiolon transition into another state if the `statement` is true.
#define checked_epsilon(statement, target) \
if (statement) \
goto e_##target;
#define epsilon_if(statement, ...) \
if (statement) { \
epsilon(__VA_ARGS__) \
}
// Transitions into the `target` FSM.
#define invoke_fsm(fsm_call, target) \
#define CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
fsm_call; \
if (ps.code > ec::trailing_character) \
return; \
ch = ps.current(); \
goto s_##target;
#define invoke_fsm_if(predicate, fsm_call, target) \
if (predicate(ch)) { \
fsm_call; \
if (ps.code > ec::trailing_character) \
#define CAF_FSM_EPSILON_IMPL3(fsm_call, target, whitelist) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
}
#define CAF_FSM_EPSILON_IMPL4(fsm_call, target, whitelist, action) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
action; \
CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
}
#define CAF_FSM_EPSILON_IMPL5(fsm_call, target, whitelist, action, error_code) \
if (::caf::detail::in_whitelist(whitelist, ch)) { \
if (!action) { \
ps.code = error_code; \
return; \
ch = ps.current(); \
goto s_##target; \
} \
CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
}
/// Makes an epsilon transition into another FSM, resuming at state `target`.
#define fsm_epsilon(...) \
CAF_PP_OVERLOAD(CAF_FSM_EPSILON_IMPL, __VA_ARGS__)(__VA_ARGS__)
#define fsm_epsilon_if(statement, ...) \
if (statement) { \
fsm_epsilon(__VA_ARGS__) \
}
......@@ -57,17 +57,15 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
});
start();
state(init) {
input(is_char<' '>, init)
input(is_char<'\t'>, init)
input(is_char<'\''>, read_chars)
transition(init, " \t")
transition(read_chars, '\'')
}
state(read_chars) {
input(is_char<'\''>, done)
checked_action(is_legal, read_chars, append(ch), ec::too_many_characters)
transition(done, '\'')
transition(read_chars, is_legal, append(ch), ec::too_many_characters)
}
term_state(done) {
input(is_char<' '>, done)
input(is_char<'\t'>, done)
transition(done, " \t")
}
fin();
}
......
......@@ -42,35 +42,32 @@ void read_bool(state<Iterator, Sentinel>& ps, Consumer& consumer) {
});
start();
state(init) {
input(is_char<' '>, init)
input(is_char<'\t'>, init)
input(is_char<'f'>, has_f)
input(is_char<'t'>, has_t)
transition(has_f, 'f')
transition(has_t, 't')
}
state(has_f) {
input(is_char<'a'>, has_fa)
transition(has_fa, 'a')
}
state(has_fa) {
input(is_char<'l'>, has_fal)
transition(has_fal, 'l')
}
state(has_fal) {
input(is_char<'s'>, has_fals)
transition(has_fals, 's')
}
state(has_fals) {
action(is_char<'e'>, done, res = false)
transition(done, 'e', res = false)
}
state(has_t) {
input(is_char<'r'>, has_tr)
transition(has_tr, 'r')
}
state(has_tr) {
input(is_char<'u'>, has_tru)
transition(has_tru, 'u')
}
state(has_tru) {
action(is_char<'e'>, done, res = true)
transition(done, 'e', res = true)
}
term_state(done) {
input(is_char<' '>, done)
input(is_char<'\t'>, done)
// nop
}
fin();
}
......
......@@ -24,7 +24,6 @@
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/read_atom.hpp"
#include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_number_or_timespan.hpp"
......@@ -56,11 +55,11 @@ template <class Iterator, class Sentinel, class Consumer>
void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&) {
start();
state(init) {
input(is_char<';'>, await_newline)
transition(await_newline, ';')
}
term_state(await_newline) {
input(is_char<'\n'>, done)
any_input(await_newline)
transition(done, '\n')
transition(await_newline)
}
term_state(done) {
// nop
......@@ -75,23 +74,19 @@ template <class Iterator, class Sentinel, class Consumer>
void read_ini_list(state<Iterator, Sentinel>& ps, Consumer& consumer) {
start();
state(init) {
action(is_char<'['>, before_value, consumer.begin_list())
transition(before_value, '[', consumer.begin_list())
}
state(before_value) {
input(is_char<' '>, before_value)
input(is_char<'\t'>, before_value)
input(is_char<'\n'>, before_value)
action(is_char<']'>, done, consumer.end_list())
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), before_value)
invoke_fsm(read_ini_value(ps, consumer), after_value)
transition(before_value, " \t\n")
transition(done, ']', consumer.end_list())
fsm_epsilon(read_ini_comment(ps, consumer), before_value, ';')
fsm_epsilon(read_ini_value(ps, consumer), after_value)
}
state(after_value) {
input(is_char<' '>, after_value)
input(is_char<'\t'>, after_value)
input(is_char<'\n'>, after_value)
input(is_char<','>, before_value)
action(is_char<']'>, done, consumer.end_list())
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), after_value)
transition(after_value, " \t\n")
transition(before_value, ',')
transition(done, ']', consumer.end_list())
fsm_epsilon(read_ini_comment(ps, consumer), after_value, ';')
}
term_state(done) {
// nop
......@@ -102,46 +97,40 @@ void read_ini_list(state<Iterator, Sentinel>& ps, Consumer& consumer) {
template <class Iterator, class Sentinel, class Consumer>
void read_ini_map(state<Iterator, Sentinel>& ps, Consumer& consumer) {
std::string key;
auto is_alnum_or_dash = [](char x) {
auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_';
};
start();
state(init) {
action(is_char<'{'>, await_key_name, consumer.begin_map())
transition(await_key_name, '{', consumer.begin_map())
}
state(await_key_name) {
input(is_char<' '>, await_key_name)
input(is_char<'\t'>, await_key_name)
input(is_char<'\n'>, await_key_name)
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), await_key_name)
action(isalnum, read_key_name, key = ch)
action(is_char<'}'>, done, consumer.end_map())
transition(await_key_name, " \t\n")
fsm_epsilon(read_ini_comment(ps, consumer), await_key_name, ';')
transition(read_key_name, alphabetic_chars, key = ch)
transition(done, '}', consumer.end_map())
}
// Reads a key of a "key=value" line.
state(read_key_name) {
action(is_alnum_or_dash, read_key_name, key += ch)
transition(read_key_name, alnum_or_dash, key += ch)
epsilon(await_assignment)
}
// Reads the assignment operator in a "key=value" line.
state(await_assignment) {
input(is_char<' '>, await_assignment)
input(is_char<'\t'>, await_assignment)
action(is_char<'='>, await_value, consumer.key(std::move(key)))
transition(await_assignment, " \t")
transition(await_value, '=', consumer.key(std::move(key)))
}
// Reads the value in a "key=value" line.
state(await_value) {
input(is_char<' '>, await_value)
input(is_char<'\t'>, await_value)
invoke_fsm(read_ini_value(ps, consumer), after_value)
transition(await_value, " \t")
fsm_epsilon(read_ini_value(ps, consumer), after_value)
}
// Waits for end-of-line after reading a value
state(after_value) {
input(is_char<' '>, after_value)
input(is_char<'\t'>, after_value)
input(is_char<'\n'>, after_value)
input(is_char<','>, await_key_name)
action(is_char<'}'>, done, consumer.end_map())
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), after_value)
transition(after_value, " \t\n")
transition(await_key_name, ',')
transition(done, '}', consumer.end_map())
fsm_epsilon(read_ini_comment(ps, consumer), after_value, ';')
}
term_state(done) {
//nop
......@@ -151,18 +140,15 @@ void read_ini_map(state<Iterator, Sentinel>& ps, Consumer& consumer) {
template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer) {
auto is_f_or_t = [](char x) {
return x == 'f' || x == 'F' || x == 't' || x == 'T';
};
start();
state(init) {
invoke_fsm_if(is_char<'"'>, read_string(ps, consumer), done)
invoke_fsm_if(is_char<'\''>, read_atom(ps, consumer), done)
invoke_fsm_if(is_char<'.'>, read_number(ps, consumer), done)
invoke_fsm_if(is_f_or_t, read_bool(ps, consumer), done)
invoke_fsm_if(isdigit, read_number_or_timespan(ps, consumer), done)
invoke_fsm_if(is_char<'['>, read_ini_list(ps, consumer), done)
invoke_fsm_if(is_char<'{'>, read_ini_map(ps, consumer), done)
fsm_epsilon(read_string(ps, consumer), done, '"')
fsm_epsilon(read_atom(ps, consumer), done, '\'')
fsm_epsilon(read_number(ps, consumer), done, '.')
fsm_epsilon(read_bool(ps, consumer), done, "ft")
fsm_epsilon(read_number_or_timespan(ps, consumer), done, decimal_chars)
fsm_epsilon(read_ini_list(ps, consumer), done, '[')
fsm_epsilon(read_ini_map(ps, consumer), done, '{')
}
term_state(done) {
// nop
......@@ -175,7 +161,7 @@ template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) {
using std::swap;
std::string tmp;
auto is_alnum_or_dash = [](char x) {
auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_';
};
bool in_section = false;
......@@ -199,61 +185,52 @@ void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) {
start();
// Scanning for first section.
term_state(init) {
input(is_char<' '>, init)
input(is_char<'\t'>, init)
input(is_char<'\n'>, init)
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), init)
input(is_char<'['>, start_section)
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) {
input(is_char<' '>, start_section)
input(is_char<'\t'>, start_section)
action(isalpha, read_section_name, tmp = ch)
transition(start_section, " \t")
transition(read_section_name, alphabetic_chars, tmp = ch)
}
// Reads a section name such as "[foo]".
state(read_section_name) {
action(is_alnum_or_dash, read_section_name, tmp += ch)
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) {
input(is_char<' '>, close_section)
input(is_char<'\t'>, close_section)
action(is_char<']'>, dispatch, begin_section())
transition(close_section, " \t")
transition(dispatch, ']', begin_section())
}
// Dispatches to read sections, comments, or key/value pairs.
term_state(dispatch) {
input(is_char<' '>, dispatch)
input(is_char<'\t'>, dispatch)
input(is_char<'\n'>, dispatch)
input(is_char<'['>, read_section_name)
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), dispatch)
action(isalnum, read_key_name, tmp = ch)
transition(dispatch, " \t\n")
transition(read_section_name, '[')
fsm_epsilon(read_ini_comment(ps, consumer), dispatch, ';')
transition(read_key_name, alphanumeric_chars, tmp = ch)
}
// Reads a key of a "key=value" line.
state(read_key_name) {
action(is_alnum_or_dash, read_key_name, tmp += ch)
transition(read_key_name, alnum_or_dash, tmp += ch)
epsilon(await_assignment)
}
// Reads the assignment operator in a "key=value" line.
state(await_assignment) {
input(is_char<' '>, await_assignment)
input(is_char<'\t'>, await_assignment)
action(is_char<'='>, await_value, emit_key())
transition(await_assignment, " \t")
transition(await_value, '=', emit_key())
}
// Reads the value in a "key=value" line.
state(await_value) {
input(is_char<' '>, await_value)
input(is_char<'\t'>, await_value)
invoke_fsm(read_ini_value(ps, consumer), await_eol)
transition(await_value, " \t")
fsm_epsilon(read_ini_value(ps, consumer), await_eol)
}
// Waits for end-of-line after reading a value
term_state(await_eol) {
input(is_char<' '>, await_eol)
input(is_char<'\t'>, await_eol)
invoke_fsm_if(is_char<';'>, read_ini_comment(ps, consumer), dispatch)
input(is_char<'\n'>, dispatch)
transition(await_eol, " \t")
fsm_epsilon(read_ini_comment(ps, consumer), dispatch, ';')
transition(dispatch, '\n')
}
fin();
}
......
......@@ -105,35 +105,34 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
// Definition of our parser FSM.
start();
state(init) {
input(is_char<' '>, init)
input(is_char<'\t'>, init)
input(is_char<'+'>, has_plus)
input(is_char<'-'>, has_minus)
input(is_char<'0'>, pos_zero)
transition(init, " \t")
transition(has_plus, '+')
transition(has_minus, '-')
transition(pos_zero, '0')
epsilon(has_plus)
}
// "+" or "-" alone aren't numbers.
state(has_plus) {
action(is_char<'.'>, leading_dot, ch_res(positive_double))
input(is_char<'0'>, pos_zero)
transition(leading_dot, '.', ch_res(positive_double))
transition(pos_zero, '0')
epsilon(pos_dec)
}
state(has_minus) {
action(is_char<'.'>, leading_dot, ch_res(negative_double))
input(is_char<'0'>, neg_zero)
transition(leading_dot, '.', ch_res(negative_double))
transition(neg_zero, '0')
epsilon(neg_dec)
}
// Disambiguate base.
term_state(pos_zero) {
input(is_ichar<'b'>, start_pos_bin)
input(is_ichar<'x'>, start_pos_hex)
action(is_char<'.'>, trailing_dot, ch_res(positive_double))
transition(start_pos_bin, "bB")
transition(start_pos_hex, "xX")
transition(trailing_dot, '.', ch_res(positive_double))
epsilon(pos_oct)
}
term_state(neg_zero) {
input(is_ichar<'b'>, start_neg_bin)
input(is_ichar<'x'>, start_neg_hex)
action(is_char<'.'>, trailing_dot, ch_res(negative_double))
transition(start_neg_bin, "bB")
transition(start_neg_hex, "xX")
transition(trailing_dot, '.', ch_res(negative_double))
epsilon(neg_oct)
}
// Binary integers.
......@@ -141,102 +140,96 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(pos_bin)
}
term_state(pos_bin) {
checked_action(is_digit<2>, pos_bin, add_ascii<2>(int_res, ch),
ec::integer_overflow)
transition(pos_bin, "01", add_ascii<2>(int_res, ch), ec::integer_overflow)
}
state(start_neg_bin) {
epsilon(neg_bin)
}
term_state(neg_bin) {
checked_action(is_digit<2>, neg_bin, sub_ascii<2>(int_res, ch),
ec::integer_underflow)
transition(neg_bin, "01", sub_ascii<2>(int_res, ch), ec::integer_underflow)
}
// Octal integers.
state(start_pos_oct) {
epsilon(pos_oct)
}
term_state(pos_oct) {
checked_action(is_digit<8>, pos_oct, add_ascii<8>(int_res, ch),
ec::integer_overflow)
transition(pos_oct, octal_chars, add_ascii<8>(int_res, ch),
ec::integer_overflow)
}
state(start_neg_oct) {
epsilon(neg_oct)
}
term_state(neg_oct) {
checked_action(is_digit<8>, neg_oct, sub_ascii<8>(int_res, ch),
ec::integer_underflow)
transition(neg_oct, octal_chars, sub_ascii<8>(int_res, ch),
ec::integer_underflow)
}
// Hexal integers.
state(start_pos_hex) {
epsilon(pos_hex)
}
term_state(pos_hex) {
checked_action(is_digit<16>, pos_hex, add_ascii<16>(int_res, ch),
ec::integer_overflow)
transition(pos_hex, hexadecimal_chars, add_ascii<16>(int_res, ch),
ec::integer_overflow)
}
state(start_neg_hex) {
epsilon(neg_hex)
}
term_state(neg_hex) {
checked_action(is_digit<16>, neg_hex, sub_ascii<16>(int_res, ch),
ec::integer_underflow)
transition(neg_hex, hexadecimal_chars, sub_ascii<16>(int_res, ch),
ec::integer_underflow)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) {
checked_action(is_digit<10>, pos_dec, add_ascii<10>(int_res, ch),
ec::integer_overflow)
action(is_ichar<'e'>, has_e, ch_res(positive_double))
action(is_char<'.'>, trailing_dot, ch_res(positive_double))
transition(pos_dec, decimal_chars, add_ascii<10>(int_res, ch),
ec::integer_overflow)
transition(has_e, "eE", ch_res(positive_double))
transition(trailing_dot, '.', ch_res(positive_double))
}
// Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) {
checked_action(is_digit<10>, neg_dec, sub_ascii<10>(int_res, ch),
ec::integer_underflow)
action(is_ichar<'e'>, has_e, ch_res(negative_double))
action(is_char<'.'>, trailing_dot, ch_res(negative_double))
transition(neg_dec, decimal_chars, sub_ascii<10>(int_res, ch),
ec::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) {
checked_action(is_digit<10>, after_dot, rd_decimal(ch),
ec::exponent_underflow)
transition(after_dot, decimal_chars, rd_decimal(ch), ec::exponent_underflow)
}
// "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) {
checked_action(is_digit<10>, after_dot, rd_decimal(ch),
ec::exponent_underflow)
input(is_ichar<'e'>, has_e)
epsilon(after_dot)
}
// Read the decimal part of a mantissa.
term_state(after_dot) {
checked_action(is_digit<10>, after_dot, rd_decimal(ch),
ec::exponent_underflow)
input(is_ichar<'e'>, has_e)
transition(after_dot, decimal_chars, rd_decimal(ch), ec::exponent_underflow)
transition(has_e, "eE")
}
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
// terminal.
state(has_e) {
input(is_char<'+'>, has_plus_after_e)
input(is_char<'-'>, has_minus_after_e)
checked_action(is_digit<10>, pos_exp, add_ascii<10>(exp, ch),
ec::exponent_overflow)
transition(has_plus_after_e, '+')
transition(has_minus_after_e, '-')
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow)
}
state(has_plus_after_e) {
checked_action(is_digit<10>, pos_exp, add_ascii<10>(exp, ch),
ec::exponent_overflow)
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow)
}
state(has_minus_after_e) {
checked_action(is_digit<10>, neg_exp, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
}
// Read a positive exponent.
term_state(pos_exp) {
checked_action(is_digit<10>, pos_exp, add_ascii<10>(exp, ch),
ec::exponent_overflow)
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow)
}
// Read a negative exponent.
term_state(neg_exp) {
checked_action(is_digit<10>, neg_exp, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
}
fin();
}
......
......@@ -73,36 +73,33 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
});
start();
state(init) {
invoke_fsm(read_number(ps, ic), has_number)
fsm_epsilon(read_number(ps, ic), has_number)
}
term_state(has_number) {
checked_epsilon(has_int(), has_integer)
checked_epsilon(has_dbl(), has_double)
epsilon_if(has_int(), has_integer)
epsilon_if(has_dbl(), has_double)
}
term_state(has_double) {
invalid_input(is_char<'u'>, ec::fractional_timespan)
invalid_input(is_char<'n'>, ec::fractional_timespan)
invalid_input(is_char<'m'>, ec::fractional_timespan)
invalid_input(is_char<'s'>, ec::fractional_timespan)
error_transition(ec::fractional_timespan, "unms")
}
term_state(has_integer) {
input(is_char<'u'>, have_u)
input(is_char<'n'>, have_n)
input(is_char<'m'>, have_m)
action(is_char<'s'>, done, res = seconds(get_int()))
transition(have_u, 'u')
transition(have_n, 'n')
transition(have_m, 'm')
transition(done, 's', res = seconds(get_int()))
}
state(have_u) {
action(is_char<'s'>, done, res = microseconds(get_int()))
transition(done, 's', res = microseconds(get_int()))
}
state(have_n) {
action(is_char<'s'>, done, res = nanoseconds(get_int()))
transition(done, 's', res = nanoseconds(get_int()))
}
state(have_m) {
input(is_char<'i'>, have_mi)
action(is_char<'s'>, done, res = milliseconds(get_int()))
transition(have_mi, 'i')
transition(done, 's', res = milliseconds(get_int()))
}
state(have_mi) {
action(is_char<'n'>, done, res = minutes(get_int()))
transition(done, 'n', res = minutes(get_int()))
}
term_state(done) {
// nop
......
......@@ -43,27 +43,25 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
});
start();
state(init) {
input(is_char<' '>, init)
input(is_char<'\t'>, init)
input(is_char<'"'>, read_chars)
transition(init, " \t")
transition(read_chars, '"')
}
state(read_chars) {
input(is_char<'\\'>, escape)
input(is_char<'"'>, done)
invalid_input(is_char<'\n'>, ec::unexpected_newline)
default_action(read_chars, res += ch)
transition(escape, '\\')
transition(done, '"')
error_transition(ec::unexpected_newline, '\n')
transition(read_chars, any_char, res += ch)
}
state(escape) {
action(is_char<'n'>, read_chars, res += '\n')
action(is_char<'r'>, read_chars, res += '\r')
action(is_char<'t'>, read_chars, res += '\t')
action(is_char<'\\'>, read_chars, res += '\\')
action(is_char<'"'>, read_chars, res += '"')
default_failure(ec::illegal_escape_sequence)
transition(read_chars, 'n', res += '\n')
transition(read_chars, 'r', res += '\r')
transition(read_chars, 't', res += '\t')
transition(read_chars, '\\', res += '\\')
transition(read_chars, '"', res += '"')
error_transition(ec::illegal_escape_sequence)
}
term_state(done) {
input(is_char<' '>, done)
input(is_char<'\t'>, done)
transition(done, " \t")
}
fin();
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
/// Concatenates x and y into a single token.
#define CAF_PP_CAT(x, y) x ## y
/// Evaluate x and y before concatenating into a single token.
#define CAF_PP_PASTE(x, y) CAF_PP_CAT(x, y)
/// Computes the number of arguments of a variadic pack.
#define CAF_PP_SIZE(...) \
CAF_PP_SIZE_I(__VA_ARGS__, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, \
52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, \
37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, \
22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, \
6, 5, 4, 3, 2, 1, )
#define CAF_PP_SIZE_I(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, \
e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, \
e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, \
e35, e36, e37, e38, e39, e40, e41, e42, e43, e44, e45, \
e46, e47, e48, e49, e50, e51, e52, e53, e54, e55, e56, \
e57, e58, e59, e60, e61, e62, e63, size, ...) \
size
/// Allows overload-like macro functions.
#define CAF_PP_OVERLOAD(PREFIX, ...) \
CAF_PP_PASTE(PREFIX, CAF_PP_SIZE(__VA_ARGS__))
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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/parser/fsm.hpp"
namespace caf {
namespace detail {
const char alphanumeric_chars[63] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const char alphabetic_chars[53] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const char hexadecimal_chars[23] = "0123456789ABCDEFabcdef";
const char decimal_chars[11] = "0123456789";
const char octal_chars[9] = "01234567";
} // namespace detail
} // namespace caf
......@@ -64,19 +64,7 @@ CAF_TEST_FIXTURE_SCOPE(read_bool_tests, fixture)
CAF_TEST(valid booleans) {
CAF_CHECK_EQUAL(p("true"), true);
CAF_CHECK_EQUAL(p(" true"), true);
CAF_CHECK_EQUAL(p(" true"), true);
CAF_CHECK_EQUAL(p("true "), true);
CAF_CHECK_EQUAL(p("true "), true);
CAF_CHECK_EQUAL(p(" true "), true);
CAF_CHECK_EQUAL(p("\t true \t\t\t "), true);
CAF_CHECK_EQUAL(p("false"), false);
CAF_CHECK_EQUAL(p(" false"), false);
CAF_CHECK_EQUAL(p(" false"), false);
CAF_CHECK_EQUAL(p("false "), false);
CAF_CHECK_EQUAL(p("false "), false);
CAF_CHECK_EQUAL(p(" false "), false);
CAF_CHECK_EQUAL(p("\t false \t\t\t "), false);
}
CAF_TEST(invalid booleans) {
......@@ -84,10 +72,12 @@ CAF_TEST(invalid booleans) {
CAF_CHECK_EQUAL(p("t"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("tr"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("tru"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p(" true"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("f"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("fa"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("fal"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("fals"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p(" false"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("tr\nue"), ec::unexpected_newline);
CAF_CHECK_EQUAL(p("trues"), ec::trailing_character);
}
......
......@@ -66,6 +66,16 @@ struct test_consumer {
}
};
struct ini_consumer {
using inner_map = std::map<std::string, config_value>;
using section_map = std::map<std::string, inner_map>;
section_map sections;
section_map::iterator current_section;
};
struct fixture {
expected<log_type> parse(std::string str, bool expect_success = true) {
detail::parser::state<std::string::iterator> res;
......
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