Commit b09395a4 authored by Dominik Charousset's avatar Dominik Charousset

Extend container parse function for maps

parent feae89a3
...@@ -19,10 +19,15 @@ ...@@ -19,10 +19,15 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <iterator>
#include <utility>
#include <vector>
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/string_view.hpp" #include "caf/string_view.hpp"
#include "caf/unit.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
...@@ -59,5 +64,57 @@ void parse(parse_state& ps, double& x); ...@@ -59,5 +64,57 @@ void parse(parse_state& ps, double& x);
void parse(parse_state& ps, atom_value& x); void parse(parse_state& ps, atom_value& 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_pair_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());
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;
}
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;
}
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
...@@ -46,9 +46,8 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer&& consumer, ...@@ -46,9 +46,8 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer&& consumer,
size_t pos = 0; size_t pos = 0;
char buf[11]; char buf[11];
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
auto is_legal = [](char c) { auto is_legal = [](char c) { return isalnum(c) || c == '_' || c == ' '; };
return isalnum(c) || c == '_' || c == ' '; auto is_legal_no_ws = [](char c) { return isalnum(c) || c == '_'; };
};
auto append = [&](char c) { auto append = [&](char c) {
if (pos == sizeof(buf) - 1) if (pos == sizeof(buf) - 1)
return false; return false;
...@@ -74,7 +73,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer&& consumer, ...@@ -74,7 +73,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer&& consumer,
transition(done, " \t") transition(done, " \t")
} }
term_state(read_unquoted_chars) { term_state(read_unquoted_chars) {
transition(read_unquoted_chars, is_legal, append(ch), pec::too_many_characters) transition(read_unquoted_chars, is_legal_no_ws, append(ch), pec::too_many_characters)
} }
fin(); fin();
// clang-format on // clang-format on
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#pragma once #pragma once
#include <cctype>
#include <cstdint> #include <cstdint>
#include "caf/pec.hpp" #include "caf/pec.hpp"
...@@ -67,6 +68,28 @@ struct state { ...@@ -67,6 +68,28 @@ struct state {
char current() const noexcept { char current() const noexcept {
return i != e ? *i : '\0'; return i != e ? *i : '\0';
} }
/// Checks whether `i == e`.
bool at_end() const noexcept {
return current() == '\0';
}
/// 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 } // namespace parser
......
...@@ -708,6 +708,27 @@ struct is_same_ish ...@@ -708,6 +708,27 @@ struct is_same_ish
template <class> template <class>
struct always_false : std::false_type {}; struct always_false : std::false_type {};
/// Utility trait for removing const inside a `map<K, V>::value_type`.
template <class T>
struct deconst_kvp_pair {
using type = T;
};
template <class K, class V>
struct deconst_kvp_pair<std::pair<const K, V>> {
using type = std::pair<K, V>;
};
template <class T>
using deconst_kvp_pair_t = typename deconst_kvp_pair<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 {};
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
...@@ -36,7 +36,7 @@ expected<T> read(string_view str) { ...@@ -36,7 +36,7 @@ expected<T> read(string_view str) {
detail::parse(ps, result); detail::parse(ps, result);
if (ps.code == pec::success) if (ps.code == pec::success)
return result; return result;
return ps.code; return make_error(ps.code, ps.line, ps.column);
} }
} // namespace } // namespace
...@@ -128,3 +128,24 @@ CAF_TEST(valid atom value) { ...@@ -128,3 +128,24 @@ CAF_TEST(valid atom value) {
CAF_CHECK_EQUAL(read<atom_value>("foo,bar"), pec::trailing_character); CAF_CHECK_EQUAL(read<atom_value>("foo,bar"), pec::trailing_character);
CAF_CHECK_EQUAL(read<atom_value>("$"), pec::unexpected_character); CAF_CHECK_EQUAL(read<atom_value>("$"), pec::unexpected_character);
} }
CAF_TEST(lists) {
using int_list = std::vector<int>;
using atom_list = std::vector<atom_value>;
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_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}}));
}
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