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