1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
// Copyright (C) 2016-2018 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)
//[ let
#include <boost/yap/yap.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/at_key.hpp>
#include <boost/hana/contains.hpp>
#include <boost/hana/keys.hpp>
#include <vector>
#include <iostream>
// Here, we introduce special let-placeholders, so we can use them along side
// the normal YAP placeholders without getting them confused.
template<long long I>
struct let_placeholder : boost::hana::llong<I>
{
};
// Replaces each let-terminal with the expression with which it was
// initialized in let(). So in 'let(_a = foo)[ _a + 1 ]', this transform will
// be used on '_a + 1' to replace '_a' with 'foo'. The map_ member holds the
// mapping of let-placeholders to their initializers.
template<typename ExprMap>
struct let_terminal_transform
{
// This matches only let-placeholders. For each one matched, we look up
// its initializer in map_ and return it.
template<long long I>
auto operator()(
boost::yap::expr_tag<boost::yap::expr_kind::terminal>,
let_placeholder<I> i)
{
// If we have an entry in map_ for this placeholder, return the value
// of the entry. Otherwise, pass i through as a terminal.
if constexpr (boost::hana::contains(
decltype(boost::hana::keys(map_))(),
boost::hana::llong_c<I>)) {
return map_[boost::hana::llong_c<I>];
} else {
return boost::yap::make_terminal(i);
}
}
ExprMap map_;
};
// As you can see below, let() is an eager function; this template is used for
// its return values. It contains the mapping from let-placeholders to
// initializer expressions used to transform the expression inside '[]' after
// a let()'. It also has an operator[]() member function that takes the
// expression inside '[]' and returns a version of it with the
// let-placeholders replaced.
template<typename ExprMap>
struct let_result
{
template<typename Expr>
auto operator[](Expr && expr)
{
return boost::yap::transform(
std::forward<Expr>(expr), let_terminal_transform<ExprMap>{map_});
}
ExprMap map_;
};
// Processes the expressions passed to let() one at a time, adding each one to
// a Hana map of hana::llong<>s to YAP expressions.
template<typename Map, typename Expr, typename... Exprs>
auto let_impl(Map && map, Expr && expr, Exprs &&... exprs)
{
static_assert(
Expr::kind == boost::yap::expr_kind::assign,
"Expressions passed to let() must be of the form placeholder = Expression");
if constexpr (sizeof...(Exprs) == 0) {
using I = typename std::remove_reference<decltype(
boost::yap::value(boost::yap::left(expr)))>::type;
auto const i = boost::hana::llong_c<I::value>;
using map_t = decltype(boost::hana::insert(
map, boost::hana::make_pair(i, boost::yap::right(expr))));
return let_result<map_t>{boost::hana::insert(
map, boost::hana::make_pair(i, boost::yap::right(expr)))};
} else {
using I = typename std::remove_reference<decltype(
boost::yap::value(boost::yap::left(expr)))>::type;
auto const i = boost::hana::llong_c<I::value>;
return let_impl(
boost::hana::insert(
map, boost::hana::make_pair(i, boost::yap::right(expr))),
std::forward<Exprs>(exprs)...);
}
}
// Takes N > 0 expressions of the form 'placeholder = expr', and returns an
// object with an overloaded operator[]().
template<typename Expr, typename... Exprs>
auto let(Expr && expr, Exprs &&... exprs)
{
return let_impl(
boost::hana::make_map(),
std::forward<Expr>(expr),
std::forward<Exprs>(exprs)...);
}
int main()
{
// Some handy terminals -- the _a and _b let-placeholders and std::cout as
// a YAP terminal.
boost::yap::expression<
boost::yap::expr_kind::terminal,
boost::hana::tuple<let_placeholder<0>>> const _a;
boost::yap::expression<
boost::yap::expr_kind::terminal,
boost::hana::tuple<let_placeholder<1>>> const _b;
auto const cout = boost::yap::make_terminal(std::cout);
using namespace boost::yap::literals;
{
auto expr = let(_a = 2)[_a + 1];
assert(boost::yap::evaluate(expr) == 3);
}
{
auto expr = let(_a = 123, _b = 456)[_a + _b];
assert(boost::yap::evaluate(expr) == 123 + 456);
}
// This prints out "0 0", because 'i' is passed as an lvalue, so its
// decrement is visible outside the let expression.
{
int i = 1;
boost::yap::evaluate(let(_a = 1_p)[cout << --_a << ' '], i);
std::cout << i << std::endl;
}
// Prints "Hello, World" due to let()'s scoping rules.
{
boost::yap::evaluate(
let(_a = 1_p, _b = 2_p)
[
// _a here is an int: 1
let(_a = 3_p) // hides the outer _a
[
cout << _a << _b // prints "Hello, World"
]
],
1, " World", "Hello,"
);
}
std::cout << "\n";
// Due to the macro-substitution style that this example uses, this prints
// "3132". Phoenix's let() prints "312", because it only evaluates '1_p
// << 3' once.
{
boost::yap::evaluate(
let(_a = 1_p << 3)
[
_a << "1", _a << "2"
],
std::cout
);
}
std::cout << "\n";
}
//]
|