• 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 #ifndef BOOST_YAP_DETAIL_TRANSFORM_HPP_INCLUDED
7 #define BOOST_YAP_DETAIL_TRANSFORM_HPP_INCLUDED
8 
9 #include <boost/yap/algorithm_fwd.hpp>
10 
11 #include <boost/hana/transform.hpp>
12 
13 #include <cassert>
14 
15 
16 namespace boost { namespace yap { namespace detail {
17 
18     template<int I, typename T, typename... Ts>
19     struct nth_element_impl
20     {
21         using type = typename nth_element_impl<I - 1, Ts...>::type;
22     };
23 
24     template<typename T, typename... Ts>
25     struct nth_element_impl<0, T, Ts...>
26     {
27         using type = T;
28     };
29 
30     template<int I, typename... Ts>
31     using nth_element = typename nth_element_impl<I, Ts...>::type;
32 
33     template<typename T, bool RemoveRefs = std::is_rvalue_reference<T>::value>
34     struct rvalue_ref_to_value;
35 
36     template<typename T>
37     struct rvalue_ref_to_value<T, true>
38     {
39         using type = typename std::remove_reference<T>::type;
40     };
41 
42     template<typename T>
43     struct rvalue_ref_to_value<T, false>
44     {
45         using type = T;
46     };
47 
48     template<typename T>
49     using rvalue_ref_to_value_t = typename rvalue_ref_to_value<T>::type;
50 
51     template<bool IsRvalueRef>
52     struct rvalue_mover
53     {
54         template<typename T>
operator ()boost::yap::detail::rvalue_mover55         constexpr decltype(auto) operator()(T && t) const
56         {
57             return static_cast<T &&>(t);
58         }
59     };
60 
61     template<>
62     struct rvalue_mover<true>
63     {
64         template<typename T>
operator ()boost::yap::detail::rvalue_mover65         constexpr std::remove_reference_t<T> operator()(T && t) const
66         {
67             return std::move(t);
68         }
69     };
70 
71     template<typename... PlaceholderArgs>
72     struct placeholder_transform_t
73     {
74         using tuple_t = hana::tuple<rvalue_ref_to_value_t<PlaceholderArgs>...>;
75 
placeholder_transform_tboost::yap::detail::placeholder_transform_t76         constexpr placeholder_transform_t(PlaceholderArgs &&... args) :
77             placeholder_args_(static_cast<PlaceholderArgs &&>(args)...)
78         {}
79 
80         template<long long I>
81         constexpr decltype(auto)
operator ()boost::yap::detail::placeholder_transform_t82         operator()(expr_tag<expr_kind::terminal>, boost::yap::placeholder<I>) const
83         {
84             static_assert(
85                 I <= decltype(hana::size(std::declval<tuple_t>()))::value,
86                 "Out of range placeholder index,");
87             using nth_type = nth_element<I - 1, PlaceholderArgs...>;
88             return as_expr<minimal_expr>(
89                 rvalue_mover<!std::is_lvalue_reference<nth_type>::value>{}(
90                     placeholder_args_[hana::llong<I - 1>{}]));
91         }
92 
93         tuple_t placeholder_args_;
94     };
95 
96     template<typename... PlaceholderArgs>
97     struct evaluation_transform_t
98     {
99         using tuple_t = hana::tuple<rvalue_ref_to_value_t<PlaceholderArgs>...>;
100 
evaluation_transform_tboost::yap::detail::evaluation_transform_t101         constexpr evaluation_transform_t(PlaceholderArgs &&... args) :
102             placeholder_args_(static_cast<PlaceholderArgs &&>(args)...)
103         {}
104 
105         template<long long I>
106         constexpr decltype(auto)
operator ()boost::yap::detail::evaluation_transform_t107         operator()(expr_tag<expr_kind::terminal>, boost::yap::placeholder<I>) const
108         {
109             static_assert(
110                 I <= decltype(hana::size(std::declval<tuple_t>()))::value,
111                 "Out of range placeholder index,");
112             using nth_type = nth_element<I - 1, PlaceholderArgs...>;
113             return rvalue_mover<!std::is_lvalue_reference<nth_type>::value>{}(
114                 placeholder_args_[hana::llong<I - 1>{}]);
115         }
116 
117         template<typename T>
operator ()boost::yap::detail::evaluation_transform_t118         constexpr decltype(auto) operator()(expr_tag<expr_kind::terminal>, T && t) const
119         {
120             return static_cast<T &&>(t);
121         }
122 
123 #define BOOST_YAP_UNARY_OPERATOR_CASE(op, op_name)                             \
124     template<typename T>                                                       \
125     constexpr decltype(auto) operator()(expr_tag<expr_kind::op_name>, T && t) const \
126     {                                                                          \
127         return op transform(                                                   \
128             as_expr<minimal_expr>(static_cast<T &&>(t)), *this);               \
129     }
130 
131         BOOST_YAP_UNARY_OPERATOR_CASE(+, unary_plus)
132         BOOST_YAP_UNARY_OPERATOR_CASE(-, negate)
133         BOOST_YAP_UNARY_OPERATOR_CASE(*, dereference)
134         BOOST_YAP_UNARY_OPERATOR_CASE(~, complement)
135         BOOST_YAP_UNARY_OPERATOR_CASE(&, address_of)
136         BOOST_YAP_UNARY_OPERATOR_CASE(!, logical_not)
137         BOOST_YAP_UNARY_OPERATOR_CASE(++, pre_inc)
138         BOOST_YAP_UNARY_OPERATOR_CASE(--, pre_dec)
139 
140         template<typename T>
operator ()boost::yap::detail::evaluation_transform_t141         constexpr decltype(auto) operator()(expr_tag<expr_kind::post_inc>, T && t) const
142         {
143             return transform(
144                 as_expr<minimal_expr>(static_cast<T &&>(t)), *this)++;
145         }
146         template<typename T>
operator ()boost::yap::detail::evaluation_transform_t147         constexpr decltype(auto) operator()(expr_tag<expr_kind::post_dec>, T && t) const
148         {
149             return transform(
150                 as_expr<minimal_expr>(static_cast<T &&>(t)), *this)--;
151         }
152 
153 #undef BOOST_YAP_UNARY_OPERATOR_CASE
154 
155 #define BOOST_YAP_BINARY_OPERATOR_CASE(op, op_name)                            \
156     template<typename T, typename U>                                           \
157     constexpr decltype(auto) operator()(expr_tag<expr_kind::op_name>, T && t, U && u) const \
158     {                                                                          \
159         return transform(as_expr<minimal_expr>(static_cast<T &&>(t)), *this)   \
160             op transform(as_expr<minimal_expr>(static_cast<U &&>(u)), *this);  \
161     }
162 
163         BOOST_YAP_BINARY_OPERATOR_CASE(<<, shift_left)
164         BOOST_YAP_BINARY_OPERATOR_CASE(>>, shift_right)
165         BOOST_YAP_BINARY_OPERATOR_CASE(*, multiplies)
166         BOOST_YAP_BINARY_OPERATOR_CASE(/, divides)
167         BOOST_YAP_BINARY_OPERATOR_CASE(%, modulus)
168         BOOST_YAP_BINARY_OPERATOR_CASE(+, plus)
169         BOOST_YAP_BINARY_OPERATOR_CASE(-, minus)
170         BOOST_YAP_BINARY_OPERATOR_CASE(<, less)
171         BOOST_YAP_BINARY_OPERATOR_CASE(>, greater)
172         BOOST_YAP_BINARY_OPERATOR_CASE(<=, less_equal)
173         BOOST_YAP_BINARY_OPERATOR_CASE(>=, greater_equal)
174         BOOST_YAP_BINARY_OPERATOR_CASE(==, equal_to)
175         BOOST_YAP_BINARY_OPERATOR_CASE(!=, not_equal_to)
176         BOOST_YAP_BINARY_OPERATOR_CASE(||, logical_or)
177         BOOST_YAP_BINARY_OPERATOR_CASE(&&, logical_and)
178         BOOST_YAP_BINARY_OPERATOR_CASE(&, bitwise_and)
179         BOOST_YAP_BINARY_OPERATOR_CASE(|, bitwise_or)
180         BOOST_YAP_BINARY_OPERATOR_CASE (^, bitwise_xor)
181 
182         // clang-format off
183 //[ evaluation_transform_comma
184         template<typename T, typename U>
operator ()boost::yap::detail::evaluation_transform_t185         constexpr decltype(auto) operator()(expr_tag<expr_kind::comma>, T && t, U && u) const
186         {
187             return transform(
188                        as_expr<minimal_expr>(static_cast<T &&>(t)), *this),
189                    transform(
190                        as_expr<minimal_expr>(static_cast<U &&>(u)), *this);
191         }
192 //]
193         // clang-format on
194 
195         BOOST_YAP_BINARY_OPERATOR_CASE(->*, mem_ptr)
196         BOOST_YAP_BINARY_OPERATOR_CASE(=, assign)
197         BOOST_YAP_BINARY_OPERATOR_CASE(<<=, shift_left_assign)
198         BOOST_YAP_BINARY_OPERATOR_CASE(>>=, shift_right_assign)
199         BOOST_YAP_BINARY_OPERATOR_CASE(*=, multiplies_assign)
200         BOOST_YAP_BINARY_OPERATOR_CASE(/=, divides_assign)
201         BOOST_YAP_BINARY_OPERATOR_CASE(%=, modulus_assign)
202         BOOST_YAP_BINARY_OPERATOR_CASE(+=, plus_assign)
203         BOOST_YAP_BINARY_OPERATOR_CASE(-=, minus_assign)
204         BOOST_YAP_BINARY_OPERATOR_CASE(&=, bitwise_and_assign)
205         BOOST_YAP_BINARY_OPERATOR_CASE(|=, bitwise_or_assign)
206         BOOST_YAP_BINARY_OPERATOR_CASE(^=, bitwise_xor_assign)
207 
208         template<typename T, typename U>
209         constexpr decltype(auto)
operator ()boost::yap::detail::evaluation_transform_t210         operator()(expr_tag<expr_kind::subscript>, T && t, U && u) const
211         {
212             return transform(
213                 as_expr<minimal_expr>(static_cast<T &&>(t)), *this)[transform(
214                 as_expr<minimal_expr>(static_cast<U &&>(u)), *this)];
215         }
216 
217 #undef BOOST_YAP_BINARY_OPERATOR_CASE
218 
219         template<typename T, typename U, typename V>
220         constexpr decltype(auto)
operator ()boost::yap::detail::evaluation_transform_t221         operator()(expr_tag<expr_kind::if_else>, T && t, U && u, V && v) const
222         {
223             return transform(as_expr<minimal_expr>(static_cast<T &&>(t)), *this)
224                        ? transform(
225                              as_expr<minimal_expr>(static_cast<U &&>(u)), *this)
226                        : transform(
227                              as_expr<minimal_expr>(static_cast<V &&>(v)),
228                              *this);
229         }
230 
231         // clang-format off
232 //[ evaluation_transform_call
233         template<typename Callable, typename... Args>
operator ()boost::yap::detail::evaluation_transform_t234         constexpr decltype(auto) operator()(
235             expr_tag<expr_kind::call>, Callable && callable, Args &&... args) const
236         {
237             return transform(as_expr<minimal_expr>(static_cast<Callable &&>(callable)), *this)(
238                 transform(as_expr<minimal_expr>(static_cast<Args &&>(args)), *this)...
239             );
240         }
241 //]
242         // clang-format on
243 
244         tuple_t placeholder_args_;
245     };
246 
247 
248     template<bool Strict, int I, bool IsExprRef>
249     struct transform_impl;
250 
251     template<
252         bool Strict,
253         typename Expr,
254         typename TransformTuple,
255         int I,
256         expr_arity Arity,
257         typename = void_t<>>
258     struct transform_expression_tag;
259 
260 
261     // Forward terminals/recurively transform noterminasl; attempted last.
262 
263     template<bool IsLvalueRef, bool IsTerminal, bool Strict>
264     struct default_transform
265     {
266         template<typename Expr, typename TransformTuple>
operator ()boost::yap::detail::default_transform267         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
268         {
269             return static_cast<Expr &&>(expr);
270         }
271     };
272 
273     template<bool IsLvalueRef, bool IsTerminal>
274     struct default_transform<IsLvalueRef, IsTerminal, true>
275     {
276         struct incomplete;
277 
278         // If you're getting an error because this function is uncallable,
279         // that's by design.  You called yap::transform_strict(expr, xfrom)
280         // and one or more subexpression of 'expr' are not callable with any
281         // overload in 'xform'.
282         template<typename Expr, typename TransformTuple>
283         constexpr incomplete operator()(Expr && expr, TransformTuple transforms) const;
284     };
285 
286     template<
287         expr_kind Kind,
288         template<expr_kind, class> class ExprTemplate,
289         typename OldTuple,
290         typename NewTuple>
make_expr_from_tuple(ExprTemplate<Kind,OldTuple> const & expr,NewTuple && tuple)291     constexpr auto make_expr_from_tuple(
292         ExprTemplate<Kind, OldTuple> const & expr, NewTuple && tuple)
293     {
294         return ExprTemplate<Kind, NewTuple>{std::move(tuple)};
295     }
296 
297     template<expr_kind Kind, typename Expr, typename NewTuple>
make_expr_from_tuple(Expr const & expr,NewTuple && tuple)298     constexpr auto make_expr_from_tuple(Expr const & expr, NewTuple && tuple)
299     {
300         return minimal_expr<Kind, NewTuple>{std::move(tuple)};
301     }
302 
303     template<typename Expr, typename Tuple, typename TransformTuple>
transform_nonterminal(Expr const & expr,Tuple && tuple,TransformTuple transforms)304     constexpr decltype(auto) transform_nonterminal(
305         Expr const & expr, Tuple && tuple, TransformTuple transforms)
306     {
307         auto transformed_tuple =
308             hana::transform(static_cast<Tuple &&>(tuple), [&](auto && element) {
309                 using element_t = decltype(element);
310                 auto const kind = remove_cv_ref_t<element_t>::kind;
311                 ::boost::yap::detail::
312                     transform_impl<false, 0, kind == expr_kind::expr_ref>
313                         xform;
314                 return xform(static_cast<element_t &&>(element), transforms);
315             });
316         auto const kind = remove_cv_ref_t<Expr>::kind;
317         return make_expr_from_tuple<kind>(expr, std::move(transformed_tuple));
318     }
319 
320     template<>
321     struct default_transform<true, false, false>
322     {
323         template<typename Expr, typename TransformTuple>
operator ()boost::yap::detail::default_transform324         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
325         {
326             return transform_nonterminal(expr, expr.elements, transforms);
327         }
328     };
329 
330     template<>
331     struct default_transform<false, false, false>
332     {
333         template<typename Expr, typename TransformTuple>
334         constexpr decltype(auto)
operator ()boost::yap::detail::default_transform335         operator()(Expr && expr, TransformTuple transforms) const
336         {
337             return transform_nonterminal(
338                 expr, std::move(expr.elements), transforms);
339         }
340     };
341 
342     // Dispatch to the next transform, or to the default transform if there is
343     // no next transform.
344 
345     template<
346         bool Strict,
347         typename Expr,
348         typename TransformTuple,
349         int I,
350         bool NextTransformExists>
351     struct next_or_default_transform
352     {
operator ()boost::yap::detail::next_or_default_transform353         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
354         {
355             // Use the next transform.
356             constexpr expr_kind kind = remove_cv_ref_t<Expr>::kind;
357             return detail::
358                 transform_impl<Strict, I + 1, kind == expr_kind::expr_ref>{}(
359                     static_cast<Expr &&>(expr), transforms);
360         }
361     };
362 
363     template<bool Strict, typename Expr, typename TransformTuple, int I>
364     struct next_or_default_transform<Strict, Expr, TransformTuple, I, false>
365     {
operator ()boost::yap::detail::next_or_default_transform366         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
367         {
368             // No next transform exists; use the default transform.
369             constexpr expr_kind kind = remove_cv_ref_t<Expr>::kind;
370             return default_transform<
371                 std::is_lvalue_reference<Expr>::value,
372                 kind == expr_kind::terminal,
373                 Strict>{}(static_cast<Expr &&>(expr), transforms);
374         }
375     };
376 
377     // Expression-matching; attempted second.
378 
379     template<
380         bool Strict,
381         typename Expr,
382         typename TransformTuple,
383         int I,
384         typename = detail::void_t<>>
385     struct transform_expression_expr
386     {
operator ()boost::yap::detail::transform_expression_expr387         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
388         {
389             // No expr-matching succeeded; use the next or default transform.
390             return next_or_default_transform<
391                 Strict,
392                 Expr,
393                 TransformTuple,
394                 I,
395                 I + 1 < decltype(hana::size(
396                             std::declval<TransformTuple>()))::value>{}(
397                 static_cast<Expr &&>(expr), transforms);
398         }
399     };
400 
401     template<bool Strict, typename Expr, typename TransformTuple, int I>
402     struct transform_expression_expr<
403         Strict,
404         Expr,
405         TransformTuple,
406         I,
407         void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])(
408             std::declval<Expr>()))>>
409     {
operator ()boost::yap::detail::transform_expression_expr410         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
411         {
412             return (*transforms[hana::llong<I>{}])(static_cast<Expr &&>(expr));
413         }
414     };
415 
416 
417     // Tag-matching; attempted first.
418 
419     template<
420         bool Strict,
421         typename Expr,
422         typename TransformTuple,
423         int I,
424         expr_arity Arity,
425         typename>
426     struct transform_expression_tag
427     {
operator ()boost::yap::detail::transform_expression_tag428         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
429         {
430             // No tag-matching succeeded; try expr-matching.
431             return transform_expression_expr<Strict, Expr, TransformTuple, I>{}(
432                 static_cast<Expr &&>(expr), transforms);
433         }
434     };
435 
436     template<typename T>
terminal_value(T && x)437     constexpr decltype(auto) terminal_value(T && x)
438     {
439         return value_impl<true>(static_cast<T &&>(x));
440     }
441 
442 
443     template<bool Strict, typename Expr, typename TransformTuple, int I>
444     struct transform_expression_tag<
445         Strict,
446         Expr,
447         TransformTuple,
448         I,
449         expr_arity::one,
450         void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])(
451             expr_tag<remove_cv_ref_t<Expr>::kind>{},
452             terminal_value(::boost::yap::value(std::declval<Expr>()))))>>
453     {
operator ()boost::yap::detail::transform_expression_tag454         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
455         {
456             return (*transforms[hana::llong<I>{}])(
457                 expr_tag<remove_cv_ref_t<Expr>::kind>{},
458                 terminal_value(
459                     ::boost::yap::value(static_cast<Expr &&>(expr))));
460         }
461     };
462 
463     template<bool Strict, typename Expr, typename TransformTuple, int I>
464     struct transform_expression_tag<
465         Strict,
466         Expr,
467         TransformTuple,
468         I,
469         expr_arity::two,
470         void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])(
471             expr_tag<remove_cv_ref_t<Expr>::kind>{},
472             terminal_value(::boost::yap::left(std::declval<Expr>())),
473             terminal_value(::boost::yap::right(std::declval<Expr>()))))>>
474     {
operator ()boost::yap::detail::transform_expression_tag475         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
476         {
477             return (*transforms[hana::llong<I>{}])(
478                 expr_tag<remove_cv_ref_t<Expr>::kind>{},
479                 terminal_value(::boost::yap::left(static_cast<Expr &&>(expr))),
480                 terminal_value(
481                     ::boost::yap::right(static_cast<Expr &&>(expr))));
482         }
483     };
484 
485     template<bool Strict, typename Expr, typename TransformTuple, int I>
486     struct transform_expression_tag<
487         Strict,
488         Expr,
489         TransformTuple,
490         I,
491         expr_arity::three,
492         void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])(
493             expr_tag<remove_cv_ref_t<Expr>::kind>{},
494             terminal_value(::boost::yap::cond(std::declval<Expr>())),
495             terminal_value(::boost::yap::then(std::declval<Expr>())),
496             terminal_value(::boost::yap::else_(std::declval<Expr>()))))>>
497     {
operator ()boost::yap::detail::transform_expression_tag498         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
499         {
500             return (*transforms[hana::llong<I>{}])(
501                 expr_tag<remove_cv_ref_t<Expr>::kind>{},
502                 terminal_value(::boost::yap::cond(static_cast<Expr &&>(expr))),
503                 terminal_value(::boost::yap::then(static_cast<Expr &&>(expr))),
504                 terminal_value(
505                     ::boost::yap::else_(static_cast<Expr &&>(expr))));
506         }
507     };
508 
509     template<typename Expr, typename Transform>
510     struct transform_call_unpacker
511     {
512         template<long long... I>
operator ()boost::yap::detail::transform_call_unpacker513         constexpr auto operator()(
514             Expr && expr,
515             Transform & transform,
516             std::integer_sequence<long long, I...>) const
517             -> decltype(transform(
518                 expr_tag<expr_kind::call>{},
519                 terminal_value(::boost::yap::get(
520                     static_cast<Expr &&>(expr), hana::llong_c<I>))...))
521         {
522             return transform(
523                 expr_tag<expr_kind::call>{},
524                 terminal_value(::boost::yap::get(
525                     static_cast<Expr &&>(expr), hana::llong_c<I>))...);
526         }
527     };
528 
529     template<typename Expr>
indices_for(Expr const & expr)530     constexpr auto indices_for(Expr const & expr)
531     {
532         constexpr long long size = decltype(hana::size(expr.elements))::value;
533         return std::make_integer_sequence<long long, size>();
534     }
535 
536     template<bool Strict, typename Expr, typename TransformTuple, int I>
537     struct transform_expression_tag<
538         Strict,
539         Expr,
540         TransformTuple,
541         I,
542         expr_arity::n,
543         void_t<decltype(
544             transform_call_unpacker<
545                 Expr,
546                 decltype(*std::declval<TransformTuple>()[hana::llong<I>{}])>{}(
547                 std::declval<Expr>(),
548                 *std::declval<TransformTuple>()[hana::llong<I>{}],
549                 indices_for(std::declval<Expr>())))>>
550     {
operator ()boost::yap::detail::transform_expression_tag551         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
552         {
553             using transform_t = decltype(*transforms[hana::llong<I>{}]);
554             return transform_call_unpacker<Expr, transform_t>{}(
555                 static_cast<Expr &&>(expr),
556                 *transforms[hana::llong<I>{}],
557                 indices_for(expr));
558         }
559     };
560 
561     template<bool Strict, int I, bool IsExprRef>
562     struct transform_impl
563     {
564         template<typename Expr, typename TransformTuple>
operator ()boost::yap::detail::transform_impl565         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
566         {
567             constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
568             return detail::transform_expression_tag<
569                 Strict,
570                 Expr,
571                 TransformTuple,
572                 I,
573                 detail::arity_of<kind>()>{}(
574                 static_cast<Expr &&>(expr), transforms);
575         }
576     };
577 
578     template<bool Strict, int I>
579     struct transform_impl<Strict, I, true>
580     {
581         template<typename Expr, typename TransformTuple>
operator ()boost::yap::detail::transform_impl582         constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const
583         {
584             return detail::transform_impl<Strict, I, false>{}(
585                 ::boost::yap::deref(static_cast<Expr &&>(expr)), transforms);
586         }
587     };
588 
589 }}}
590 
591 #endif
592