Warning, file /plasma/lancelot/src/voy/dsl.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2018 Ivan Čukić <ivan.cukic(at)kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #ifndef VOY_DSL_H 0008 #define VOY_DSL_H 0009 0010 // STL 0011 #include <memory> 0012 #include <tuple> 0013 0014 // Self 0015 #include "utils.h" 0016 #include "dsl/node_tags.h" 0017 #include "dsl/node_traits.h" 0018 #include "engine/event_loop.h" 0019 0020 namespace voy::dsl { 0021 0022 using node_traits::is_connection_expr, 0023 node_traits::is_node, 0024 node_traits::is_source, 0025 node_traits::is_sink, 0026 node_traits::node_category, 0027 node_traits::has_with_continuation; 0028 0029 // We need to be able to store the connected graph paths (pipelines) 0030 // inside of ordinary classes, so we will return a type-erased pipeline 0031 // to the caller once the path is complete 0032 class pipeline { 0033 public: 0034 virtual ~pipeline() {} 0035 virtual void init() = 0; 0036 0037 using ptr = std::unique_ptr<pipeline>; 0038 }; 0039 0040 namespace detail { 0041 // The actual type of a pipeline 0042 template <typename T> 0043 struct pipeline_impl: pipeline { 0044 explicit pipeline_impl(T&& content) noexcept 0045 : node{std::move(content.node)} 0046 { 0047 } 0048 0049 void init() override 0050 { 0051 node.init(); 0052 } 0053 0054 T node; 0055 }; 0056 0057 0058 0059 // In order to use the fold expressions, we need to provide an operator 0060 // on which to fold. We can create a wrapper type with operator% 0061 // defined for it 0062 template <typename Node> 0063 struct node_wrapper: utils::non_copyable { 0064 voy_assert_value_type(Node); 0065 0066 node_wrapper(Node node) 0067 : node{std::move(node)} 0068 { 0069 } 0070 0071 node_wrapper(node_wrapper&& other) = delete; 0072 void operator=(node_wrapper&&) = delete; 0073 0074 void init() 0075 { 0076 node.init(); 0077 } 0078 0079 Node node; 0080 }; 0081 0082 0083 template <typename Node> 0084 auto make_node_wrapper(Node&& node) 0085 { 0086 voy_assert_value_type(Node); 0087 return node_wrapper<Node>{std::move(node)}; 0088 } 0089 0090 0091 template <typename Left, typename Right> 0092 decltype(auto) operator% (node_wrapper<Left>&& receiver, 0093 node_wrapper<Right>&& sender) 0094 { 0095 voy_assert_value_type(Left); 0096 voy_assert_value_type(Right); 0097 0098 return make_node_wrapper( 0099 std::move(sender.node).with_continuation(std::move(receiver.node)) 0100 ); 0101 } 0102 0103 0104 // Goes through all the items in a tuple, and connects one by one 0105 template <typename... Args, size_t... Idx> 0106 pipeline::ptr connect_all(std::tuple<Args...> items, 0107 std::index_sequence<Idx...>) 0108 { 0109 return std::make_unique< 0110 decltype(pipeline_impl((... % make_node_wrapper(std::get<Idx>(std::move(items)))))) 0111 > ((... % make_node_wrapper(std::get<Idx>(std::move(items))))); 0112 } 0113 0114 0115 0116 // The connection_expr class represents one pipe operation 0117 // in the AST where the left and right arguments can be either 0118 // compound expressions themselves, or single nodes 0119 template <typename NodeCategory, typename LeftGraph, typename RightGraph> 0120 struct connection_expr { 0121 voy_assert_value_type(LeftGraph); 0122 voy_assert_value_type(RightGraph); 0123 0124 LeftGraph left; 0125 RightGraph right; 0126 0127 // An expression is also a graph node 0128 using node_category = NodeCategory; 0129 // ... but we still need to be able to differentiate it from normal nodes 0130 using connection_expr_tag = node_category; 0131 0132 // Generates a tuple of all nodes in an expression from right to left, 0133 // that is, from the sink to the source 0134 auto collect_graph_nodes() 0135 { 0136 auto collect_left_graph_nodes = [this] 0137 { 0138 if constexpr (is_connection_expr<LeftGraph>) { 0139 return left.collect_graph_nodes(); 0140 } else { 0141 return std::make_tuple(std::move(left)); 0142 } 0143 }; 0144 0145 auto collect_right_graph_nodes = [this] 0146 { 0147 if constexpr (is_connection_expr<RightGraph>) { 0148 return right.collect_graph_nodes(); 0149 } else { 0150 return std::make_tuple(std::move(right)); 0151 } 0152 }; 0153 0154 return std::tuple_cat(collect_right_graph_nodes(), 0155 collect_left_graph_nodes()); 0156 } 0157 0158 connection_expr(LeftGraph left, RightGraph right) 0159 : left{std::move(left)} 0160 , right{std::move(right)} 0161 { 0162 } 0163 0164 auto evaluate() 0165 { 0166 if constexpr (std::is_same_v<void, node_category>) { 0167 auto sink_to_source_items = collect_graph_nodes(); 0168 0169 auto result = connect_all(std::move(sink_to_source_items), 0170 std::make_index_sequence< 0171 std::tuple_size_v< 0172 std::decay_t<decltype(sink_to_source_items)> 0173 > 0174 >()); 0175 0176 voy::event_loop::invoke_later([result = result.get()] { 0177 result->init(); 0178 }); 0179 0180 return result; 0181 0182 } else { 0183 voy_fail(node_category, "'evaluate' can only be called on a complete path"); 0184 } 0185 } 0186 }; 0187 0188 0189 template < typename NodeCategory 0190 , typename LeftGraph 0191 , typename RightGraph 0192 > 0193 auto make_connection_expr(LeftGraph&& left, RightGraph&& right) 0194 { 0195 return connection_expr < NodeCategory 0196 , traits::remove_cvref_t<LeftGraph> 0197 , traits::remove_cvref_t<RightGraph> 0198 > { voy_fwd(left), voy_fwd(right) }; 0199 } 0200 0201 0202 template < typename Left 0203 , typename Right 0204 , typename LeftVal = traits::remove_cvref_t<Left> 0205 , typename RightVal = traits::remove_cvref_t<Right> 0206 > 0207 decltype(auto) connect_streams(Left&& left, Right&& right) 0208 { 0209 static_assert(is_node<Left>, "The left needs to be a node"); 0210 static_assert(is_node<Right>, "The right needs to be a node"); 0211 static_assert( 0212 is_connection_expr<Left> || 0213 has_with_continuation<Left>, 0214 "The left node needs to be a connection expression, or to have with_continuation member function"); 0215 0216 #define MAKE(Type) \ 0217 make_connection_expr<Type, LeftVal, RightVal>(std::move(left), \ 0218 std::move(right)) 0219 0220 if constexpr (!is_source<Left> && !is_sink<Right>) { 0221 0222 return MAKE(transformation_node_tag); 0223 0224 } else if constexpr (is_source<Left> && !is_sink<Right>) { 0225 0226 return MAKE(node_category<Left>); 0227 0228 } else if constexpr (!is_source<Left> && is_sink<Right>) { 0229 0230 return MAKE(node_category<Right>); 0231 0232 } else { 0233 0234 // If we have both a sink and a source, we can connect the 0235 // nodes in this path of the graph 0236 return MAKE(void).evaluate(); 0237 0238 } 0239 0240 #undef MAKE 0241 } 0242 } // namespace detail 0243 0244 0245 template < typename Left 0246 , typename Right 0247 , voy_require( 0248 is_node<Left> && is_node<Right> 0249 ) 0250 > 0251 [[nodiscard]] decltype(auto) operator| (Left&& left, Right&& right) 0252 { 0253 return detail::connect_streams(voy_fwd(left), voy_fwd(right)); 0254 } 0255 0256 0257 } // namespace voy::dsl 0258 0259 #endif // include guard 0260