diff --git a/include/boost/parser/detail/hl.hpp b/include/boost/parser/detail/hl.hpp new file mode 100644 index 00000000..76407d68 --- /dev/null +++ b/include/boost/parser/detail/hl.hpp @@ -0,0 +1,329 @@ +#ifndef BOOST_PARSER_DETAIL_HL_HPP +#define BOOST_PARSER_DETAIL_HL_HPP + +#include + +#include +#include +#include + + +namespace boost { namespace parser { namespace detail::hl { + + // Boost.Hana lite. + + struct forward + { + template + decltype(auto) operator()(T && t) + { + return (T &&) t; + } + }; + + + // for_each + + template + void for_each_impl( + Tuple const & t, F && f, std::integer_sequence) + { + int _[] = {0, (f(std::get(t)), 0)...}; + (void)_; + } + template< + typename F, + typename Tuple, + std::size_t... I, + typename Enable = std::enable_if_t>> + void + for_each_impl(Tuple && t, F && f, std::integer_sequence) + { + int _[] = {0, (f(std::move(std::get(t))), 0)...}; + (void)_; + } + + template + void for_each(std::tuple && t, F && f) + { + hl::for_each_impl( + std::move(t), + (F &&) f, + std::make_integer_sequence()); + } + template + void for_each(std::tuple const & t, F && f) + { + hl::for_each_impl( + t, + (F &&) f, + std::make_integer_sequence()); + } + + + // transform + + template + auto transform_impl( + Tuple const & t, F && f, std::integer_sequence) + { + return std::tuple< + std::decay_t(t)))>...>{ + f(std::get(t))...}; + } + template< + int offset, + typename F, + typename Tuple, + std::size_t... I, + typename Enable = std::enable_if_t>> + auto + transform_impl(Tuple && t, F && f, std::integer_sequence) + { + return std::tuple< + std::decay_t(t))))>...>{ + f(std::move(std::get(t)))...}; + } + + template + auto transform(std::tuple && t, F && f) + { + return hl::transform_impl<0>( + std::move(t), + (F &&) f, + std::make_integer_sequence()); + } + template + auto transform(std::tuple const & t, F && f) + { + return hl::transform_impl<0>( + t, + (F &&) f, + std::make_integer_sequence()); + } + + + // fold_left + + template + struct fold_left_dispatch + { + template + static auto call(std::tuple const & t, State && s, F const & f) + { + return fold_left_dispatch::call( + t, f((State &&) s, std::get(t)), f); + } + }; + template + struct fold_left_dispatch + { + template + static auto call(std::tuple const & t, State && s, F const & f) + { + return (State &&) s; + } + }; + + template + auto fold_left(std::tuple const & t, State && s, F const & f) + { + return hl::fold_left_dispatch<0, sizeof...(Args)>::call( + t, (State &&) s, (F &&) f); + } + + + // size + + template + constexpr auto size(std::tuple const & t) + { + return sizeof...(Args); + } + + + // contains + + template + using comparable = decltype(std::declval() == std::declval()); + + struct typesafe_equals + { + template + bool operator()(T const & t, U const & u) + { + if constexpr (detail::is_detected::value) { + return t == u; + } else { + return false; + } + } + }; + + template + bool contains_impl( + Tuple const & t, T const & x, std::integer_sequence) + { + typesafe_equals eq; + return (eq(std::get(t), x) || ...); + } + + template + bool contains(std::tuple & t, T const & x) + { + return contains_impl( + t, x, std::make_integer_sequence()); + } + + + // front, back + + template + decltype(auto) front(std::tuple & t) + { + return std::get<0>(t); + } + template + decltype(auto) front(std::tuple const & t) + { + return std::get<0>(t); + } + template + decltype(auto) back(std::tuple & t) + { + return std::get(t); + } + template + decltype(auto) back(std::tuple const & t) + { + return std::get(t); + } + + + // drop_front + + template + auto drop_front(std::tuple && t) + { + return hl::transform_impl<1>( + std::move(t), + forward{}, + std::make_integer_sequence()); + } + template + auto drop_front(std::tuple const & t) + { + return hl::transform_impl<1>( + t, + forward{}, + std::make_integer_sequence()); + } + + + // drop_back + + template + auto drop_back(std::tuple && t) + { + return hl::transform_impl<0>( + std::move(t), + forward{}, + std::make_integer_sequence()); + } + template + auto drop_back(std::tuple const & t) + { + return hl::transform_impl<0>( + t, + forward{}, + std::make_integer_sequence()); + } + + + // first, second + + template + decltype(auto) first(std::tuple & t) + { + return std::get<0>(t); + } + template + decltype(auto) first(std::tuple const & t) + { + return std::get<0>(t); + } + template + decltype(auto) second(std::tuple & t) + { + return std::get<1>(t); + } + template + decltype(auto) second(std::tuple const & t) + { + return std::get<1>(t); + } + + + // zip + + template + decltype(auto) make_zip_elem(Tuples const &... ts) + { + return std::make_tuple(std::get(ts)...); + } + + template + auto zip_impl(std::index_sequence, Tuples const &... ts) + { + return std::make_tuple(hl::make_zip_elem(ts...)...); + } + + template + auto zip(Tuple const & t, Tuples const &... ts) + { + return hl::zip_impl( + std::make_integer_sequence< + std::size_t, + std::tuple_size>::value>(), + t, + ts...); + } + + + // append + + template + auto append(std::tuple && t, T && x) + { + return std::tuple_cat(std::move(t), std::make_tuple((T &&) x)); + } + template + auto append(std::tuple const & t, T && x) + { + return std::tuple_cat(t, std::make_tuple((T &&) x)); + } + + + // prepend + + template + auto prepend(std::tuple && t, T && x) + { + return std::tuple_cat(std::make_tuple((T &&) x), std::move(t)); + } + template + auto prepend(std::tuple const & t, T && x) + { + return std::tuple_cat(std::make_tuple((T &&) x), t); + } + + // TODO: In parser namespace, get(t, i), where t is a tuple and i is a + // std::integral_constant const &. + + // TODO: In parser namespace, a literal for N_c, a la hana's literal, + // except that it produces a type derived from + // std::integral_constant. + +}}} + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fc68b0ef..c9128cb2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,6 +55,7 @@ endmacro() # add_test_executable(${test_name}) #endforeach() +add_test_executable(hl) add_test_executable(parser_lazy_params) add_test_executable(parser_if_switch) add_test_executable(parser_rule) diff --git a/test/hl.cpp b/test/hl.cpp new file mode 100644 index 00000000..9464d02b --- /dev/null +++ b/test/hl.cpp @@ -0,0 +1,198 @@ +/** + * Copyright (C) 2020 T. Zachary Laine + * + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include + +#include + + +using namespace boost::parser; + +TEST(hl, for_each) +{ + { + std::tuple t{"foo", 4}; + std::ostringstream oss; + detail::hl::for_each(t, [&](auto x) { oss << x << ' '; }); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(oss.str(), "foo 4 "); + } + { + std::tuple t{"foo", 4}; + std::ostringstream oss; + detail::hl::for_each(std::move(t), [&](auto x) { oss << x << ' '; }); + EXPECT_EQ(std::get<0>(t), ""); + EXPECT_EQ(oss.str(), "foo 4 "); + } +} + +TEST(hl, transform) +{ + { + std::tuple t{"foo", 4}; + auto t2 = detail::hl::transform( + t, [&](auto x) { return std::optional{x}; }); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(std::get<0>(t2), std::optional{"foo"}); + EXPECT_EQ(std::get<1>(t2), std::optional{4}); + } + { + std::tuple t{"foo", 4}; + auto t2 = detail::hl::transform(std::move(t), [&](auto x) { + return std::optional{x}; + }); + EXPECT_EQ(std::get<0>(t), ""); + EXPECT_EQ(std::get<0>(t2), std::optional{"foo"}); + EXPECT_EQ(std::get<1>(t2), std::optional{4}); + } +} + +TEST(hl, fold_left) +{ + std::tuple t{"foo", 4}; + auto t2 = detail::hl::fold_left( + t, std::tuple<>{}, [](auto const & state, auto x) { + return detail::hl::append(state, x); + }); + EXPECT_EQ(t, t2); +} + +TEST(hl, size) +{ + { + std::tuple t{"foo", 4}; + EXPECT_EQ(detail::hl::size(t), 2u); + } + { + std::tuple t{"foo", 4}; + constexpr std::size_t size = detail::hl::size(t); + EXPECT_EQ(size, 2u); + } +} + +TEST(hl, contains) +{ + std::tuple t{"foo", 4, 3.0}; + EXPECT_TRUE(detail::hl::contains(t, 3.0)); + EXPECT_TRUE(detail::hl::contains(t, "foo")); + EXPECT_TRUE(detail::hl::contains(t, std::string_view("foo"))); + EXPECT_FALSE(detail::hl::contains(t, std::string_view("banana"))); +} + +TEST(hl, front_back) +{ + std::tuple t{"foo", 4}; + EXPECT_EQ(detail::hl::front(t), "foo"); + EXPECT_EQ(detail::hl::back(t), 4); +} + +TEST(hl, drop_front) +{ + { + std::tuple t{"foo", 4}; + auto t2 = detail::hl::drop_front(t); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(std::get<0>(t2), 4); + } + { + std::tuple t{"foo", 4, 3.0}; + auto t2 = detail::hl::drop_front(std::move(t)); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(std::get<0>(t2), 4); + EXPECT_EQ(std::get<1>(t2), 3.0); + } +} + +TEST(hl, drop_back) +{ + { + std::tuple t{"foo", 4}; + auto t2 = detail::hl::drop_back(t); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(std::get<0>(t2), "foo"); + } + { + std::tuple t{"foo", 4, 3.0}; + auto t2 = detail::hl::drop_back(std::move(t)); + EXPECT_EQ(std::get<0>(t), ""); + EXPECT_EQ(std::get<0>(t2), "foo"); + EXPECT_EQ(std::get<1>(t2), 4); + } +} + +TEST(hl, first_second) +{ + std::tuple t{"foo", 4}; + EXPECT_EQ(detail::hl::first(t), "foo"); + EXPECT_EQ(detail::hl::second(t), 4); +} + +TEST(hl, append) +{ + { + std::tuple t{"foo"}; + auto t2 = detail::hl::append(t, 4); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(std::get<0>(t2), "foo"); + EXPECT_EQ(std::get<1>(t2), 4); + } + { + std::tuple t{"foo", 4}; + auto t2 = detail::hl::append(std::move(t), 3.0); + EXPECT_EQ(std::get<0>(t), ""); + EXPECT_EQ(std::get<0>(t2), "foo"); + EXPECT_EQ(std::get<1>(t2), 4); + EXPECT_EQ(std::get<2>(t2), 3.0); + } +} + +TEST(hl, prepend) +{ + { + std::tuple t{"foo"}; + auto t2 = detail::hl::prepend(t, 4); + EXPECT_EQ(std::get<0>(t), "foo"); + EXPECT_EQ(std::get<0>(t2), 4); + EXPECT_EQ(std::get<1>(t2), "foo"); + } + { + std::tuple t{"foo", 4}; + auto t2 = detail::hl::prepend(std::move(t), 3.0); + EXPECT_EQ(std::get<0>(t), ""); + EXPECT_EQ(std::get<0>(t2), 3.0); + EXPECT_EQ(std::get<1>(t2), "foo"); + EXPECT_EQ(std::get<2>(t2), 4); + } +} + +TEST(hl, zip) +{ + { + std::tuple t1{"foo", 4}; + std::tuple t2{3.0, "bar"}; + auto t3 = detail::hl::zip(t1, t2); + EXPECT_EQ( + std::get<0>(t3), (std::tuple("foo", 3.0))); + EXPECT_EQ(std::get<1>(t3), (std::tuple(4, "bar"))); + } + { + std::tuple t1{"foo", 4}; + std::tuple t2{3.0, "bar"}; + std::tuple t3{"baz", "baz"}; + auto t4 = detail::hl::zip(t1, t2, t3); + EXPECT_EQ( + std::get<0>(t4), + (std::tuple("foo", 3.0, "baz"))); + EXPECT_EQ( + std::get<1>(t4), + (std::tuple(4, "bar", "baz"))); + } +}