// 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) #include #include #include #include #include #include #include template struct let_placeholder : boost::hana::llong { }; template struct let_terminal_transform { template auto operator()(boost::yap::expr_tag, T && t) { return boost::yap::as_expr(std::forward(t)); } template auto operator()( boost::yap::expr_tag, let_placeholder i) { 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); } } template auto operator()(boost::yap::expr_tag, Args &&... args) { return boost::yap::make_expression( boost::yap::transform(boost::yap::as_expr(args), *this)...); } template auto operator()( boost::yap::expr_tag, CallableExpr callable, Arg &&... arg) { return boost::yap::make_expression( boost::yap::as_expr(callable), boost::yap::transform(boost::yap::as_expr(arg), *this)...); } ExprMap map_; }; template struct let_result { template auto operator[](Expr && expr) { return boost::yap::transform( std::forward(expr), let_terminal_transform{map_}); } ExprMap map_; }; 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)...); } } template auto let(Expr && expr, Exprs &&... exprs) { return let_impl( boost::hana::make_map(), std::forward(expr), std::forward(exprs)...); } int main() { 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); } { int i = 1; boost::yap::evaluate(let(_a = 1_p)[cout << --_a << ' '], i); std::cout << i << std::endl; } { 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," ); } }