123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // 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";
- }
- //]
|