// 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 #include #include #include #include #include #include // Here, we introduce special let-placeholders, so we can use them along side // the normal YAP placeholders without getting them confused. template struct let_placeholder : boost::hana::llong { }; // 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 struct let_terminal_transform { // This matches only let-placeholders. For each one matched, we look up // its initializer in map_ and return it. template auto operator()( boost::yap::expr_tag, let_placeholder 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)) { return map_[boost::hana::llong_c]; } 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 struct let_result { template auto operator[](Expr && expr) { return boost::yap::transform( std::forward(expr), let_terminal_transform{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 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::type; auto const i = boost::hana::llong_c; using map_t = decltype(boost::hana::insert( map, boost::hana::make_pair(i, boost::yap::right(expr)))); return let_result{boost::hana::insert( map, boost::hana::make_pair(i, boost::yap::right(expr)))}; } else { using I = typename std::remove_reference::type; auto const i = boost::hana::llong_c; return let_impl( boost::hana::insert( map, boost::hana::make_pair(i, boost::yap::right(expr))), std::forward(exprs)...); } } // Takes N > 0 expressions of the form 'placeholder = expr', and returns an // object with an overloaded operator[](). template auto let(Expr && expr, Exprs &&... exprs) { return let_impl( boost::hana::make_map(), std::forward(expr), std::forward(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>> const _a; boost::yap::expression< boost::yap::expr_kind::terminal, boost::hana::tuple>> 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"; } //]