summaryrefslogtreecommitdiffstats
path: root/server/src/xml.cppm
diff options
context:
space:
mode:
authorRutger Broekhoff2026-08-28 21:12:55 +0200
committerRutger Broekhoff2026-08-28 21:12:55 +0200
commita5d95afb96eb9a3b65d82f5f84bf8e4a4fb4c8ac (patch)
treeb4e0ec57c3799e9f649c6bfe27cb6c3313b97364 /server/src/xml.cppm
parent973aec43ea54bbf95b64fbcb636403401d1ca60e (diff)
downloadroutemon-a5d95afb96eb9a3b65d82f5f84bf8e4a4fb4c8ac.tar.gz
routemon-a5d95afb96eb9a3b65d82f5f84bf8e4a4fb4c8ac.zip
clang-format C++ sources
Diffstat (limited to 'server/src/xml.cppm')
-rw-r--r--server/src/xml.cppm1166
1 files changed, 682 insertions, 484 deletions
diff --git a/server/src/xml.cppm b/server/src/xml.cppm
index 957f149..ff27c5c 100644
--- a/server/src/xml.cppm
+++ b/server/src/xml.cppm
@@ -23,610 +23,808 @@ static_assert(std::is_same_v<XML_Char, char>);
23 23
24namespace routemon::xml { 24namespace routemon::xml {
25 25
26 struct qname_view { 26struct qname_view
27 std::string_view ns_uri; 27{
28 std::string_view local; 28 std::string_view ns_uri;
29 std::string_view local;
29 30
30 auto operator==(qname_view const& rhs) const -> bool; 31 auto operator==(qname_view const& rhs) const -> bool;
31 }; 32};
32 33
33 namespace detail { 34namespace detail {
34 35
35 constexpr auto qname_sep = '\xFF'; 36constexpr auto qname_sep = '\xFF';
36 auto split_name(char const* name) noexcept -> qname_view { 37auto split_name(char const* name) noexcept -> qname_view
37 auto [l, r] = util::split_on(name, qname_sep); 38{
38 if (r) return { .ns_uri = l, .local = *r }; 39 auto [l, r] = util::split_on(name, qname_sep);
39 else return { .ns_uri = std::string_view{}, .local = l }; 40 if (r)
40 } 41 return {.ns_uri = l, .local = *r};
42 else
43 return {.ns_uri = std::string_view{}, .local = l};
44}
41 45
42 } // namespace detail 46} // namespace detail
43 47
44 class attribute_view_iterator { 48class attribute_view_iterator
45 std::string_view default_ns_uri_; 49{
46 char const* const* attrs_; 50 std::string_view default_ns_uri_;
51 char const* const* attrs_;
47 52
48 auto advance() -> void { 53 auto advance() -> void { attrs_ += 2; }
49 attrs_ += 2;
50 }
51 54
52 public: 55public:
53 using difference_type = std::ptrdiff_t; 56 using difference_type = std::ptrdiff_t;
54 using value_type = std::pair<qname_view, char const*>; 57 using value_type = std::pair<qname_view, char const*>;
55 58
56 struct sentinel { 59 struct sentinel
57 friend constexpr auto operator==(attribute_view_iterator const& it, sentinel) noexcept -> bool { 60 {
58 return !*it.attrs_; 61 friend constexpr auto
59 } 62 operator==(attribute_view_iterator const& it, sentinel) noexcept -> bool
60 }; 63 {
64 return !*it.attrs_;
65 }
66 };
61 67
62 inline explicit attribute_view_iterator(std::string_view default_ns_uri, char const* const* attrs) 68 inline explicit attribute_view_iterator(
63 : default_ns_uri_{default_ns_uri}, attrs_{attrs} 69 std::string_view default_ns_uri, char const* const* attrs)
64 {} 70 : default_ns_uri_{default_ns_uri}, attrs_{attrs}
71 {
72 }
65 73
66 inline auto operator*() const -> std::pair<qname_view, char const*> { 74 inline auto operator*() const -> std::pair<qname_view, char const*>
67 if (!*attrs_) 75 {
68 throw std::runtime_error{"end of attribute list"}; 76 if (!*attrs_)
69 auto qname = detail::split_name(attrs_[0]); 77 throw std::runtime_error{"end of attribute list"};
70 if (qname.ns_uri.empty()) 78 auto qname = detail::split_name(attrs_[0]);
71 qname.ns_uri = default_ns_uri_; 79 if (qname.ns_uri.empty())
72 return std::make_pair(qname, attrs_[1]); 80 qname.ns_uri = default_ns_uri_;
73 } 81 return std::make_pair(qname, attrs_[1]);
82 }
74 83
75 // Pre-increment 84 // Pre-increment
76 inline auto operator++() -> attribute_view_iterator& { 85 inline auto operator++() -> attribute_view_iterator&
77 advance(); 86 {
78 return *this; 87 advance();
79 } 88 return *this;
89 }
80 90
81 // Post-increment 91 // Post-increment
82 inline auto operator++(int) -> attribute_view_iterator { 92 inline auto operator++(int) -> attribute_view_iterator
83 auto pre = *this; 93 {
84 advance(); 94 auto pre = *this;
85 return pre; 95 advance();
86 } 96 return pre;
87 }; 97 }
88 static_assert(std::input_iterator<attribute_view_iterator>); 98};
99static_assert(std::input_iterator<attribute_view_iterator>);
89 100
90 class attribute_view : std::ranges::view_base { 101class attribute_view : std::ranges::view_base
91 std::string_view default_ns_uri_; 102{
92 char const* const* attrs_; 103 std::string_view default_ns_uri_;
104 char const* const* attrs_;
93 105
94 public: 106public:
95 inline explicit attribute_view(std::string_view default_ns_uri, char const** attrs) 107 inline explicit attribute_view(
96 : default_ns_uri_{default_ns_uri}, attrs_{const_cast<char const* const*>(attrs)} 108 std::string_view default_ns_uri, char const** attrs)
97 {} 109 : default_ns_uri_{default_ns_uri},
110 attrs_{const_cast<char const* const*>(attrs)}
111 {
112 }
98 113
99 [[nodiscard]] inline auto begin() const -> attribute_view_iterator { 114 [[nodiscard]] inline auto begin() const -> attribute_view_iterator
100 return attribute_view_iterator{default_ns_uri_, attrs_}; 115 {
101 } 116 return attribute_view_iterator{default_ns_uri_, attrs_};
117 }
102 118
103 [[nodiscard]] inline auto end() const -> attribute_view_iterator::sentinel { 119 [[nodiscard]] inline auto end() const -> attribute_view_iterator::sentinel
104 return {}; 120 {
105 } 121 return {};
122 }
106 123
107 inline auto lookup(qname_view want) -> std::optional<util::not_null<util::lazy_zstring_view>> { 124 inline auto lookup(qname_view want)
108 for (auto const& [name, v] : *this) { 125 -> std::optional<util::not_null<util::lazy_zstring_view>>
109 if (name == want) { 126 {
110 return util::not_null{util::lazy_zstring_view{v}}; 127 for (auto const& [name, v] : *this)
111 } 128 {
129 if (name == want)
130 {
131 return util::not_null{util::lazy_zstring_view{v}};
112 } 132 }
113 return std::nullopt;
114 } 133 }
115 }; 134 return std::nullopt;
116 static_assert(std::ranges::input_range<attribute_view>); 135 }
136};
137static_assert(std::ranges::input_range<attribute_view>);
117 138
118 template<class T> class promise; 139template <class T>
140class promise;
119 141
120 template<class T> 142template <class T>
121 struct [[clang::coro_await_elidable, clang::coro_return_type]] parser { 143struct [[clang::coro_await_elidable, clang::coro_return_type]] parser
122 using promise_type = promise<T>; 144{
123 using result_type = promise_type::result_type; 145 using promise_type = promise<T>;
124 using handle_type = std::coroutine_handle<promise_type>; 146 using result_type = promise_type::result_type;
147 using handle_type = std::coroutine_handle<promise_type>;
125 148
126 private: 149private:
127 handle_type h_; 150 handle_type h_;
128 151
129 public: 152public:
130 explicit parser(handle_type h) 153 explicit parser(handle_type h) : h_{h} { assert(h); }
131 : h_{h}
132 { assert(h); }
133 154
134 parser(const parser&) = delete; 155 parser(const parser&) = delete;
135 parser(parser&& c) noexcept 156 parser(parser&& c) noexcept : h_{std::exchange(c.h_, nullptr)} {}
136 : h_{std::exchange(c.h_, nullptr)} 157 auto operator=(const parser&) -> parser& = delete;
137 {} 158 auto operator=(parser&&) -> parser& = delete;
138 auto operator=(const parser&) -> parser& = delete;
139 auto operator=(parser&&) -> parser& = delete;
140 159
141 [[nodiscard]] auto promise() const -> promise_type& { 160 [[nodiscard]] auto promise() const -> promise_type& { return h_.promise(); }
142 return h_.promise();
143 }
144 161
145 ~parser() { 162 ~parser()
146 if (h_) h_.destroy(); 163 {
147 } 164 if (h_)
148 }; 165 h_.destroy();
166 }
167};
149 168
150 struct start_element_event { 169struct start_element_event
151 qname_view name; 170{
152 attribute_view attrs; 171 qname_view name;
153 }; 172 attribute_view attrs;
154 struct end_element_event { 173};
155 qname_view name;
156 };
157 struct character_data_event {
158 std::string_view data;
159 };
160 struct processing_instructions_event {
161 util::lazy_zstring_view target;
162 util::lazy_zstring_view data;
163 };
164 struct xml_decl_event {
165 util::lazy_zstring_view version;
166 util::lazy_zstring_view encoding;
167 std::optional<bool> standalone;
168 };
169 struct eof_event {};
170 using event = std::variant<start_element_event,
171 end_element_event,
172 character_data_event,
173 processing_instructions_event,
174 xml_decl_event,
175 eof_event>;
176 template<class T>
177 concept event_type = requires(event ev) { std::get<T>(ev); };
178 174
179 class executor; 175struct end_element_event
180 using executor_ref = util::not_null<executor*>; 176{
177 qname_view name;
178};
181 179
182 struct current_event_t { 180struct character_data_event
183 executor_ref executor; 181{
184 }; 182 std::string_view data;
185 auto current_event(executor_ref executor) -> current_event_t { 183};
186 return current_event_t{executor};
187 }
188 184
189 class promise_base { 185struct processing_instructions_event
190 executor_ref executor_; 186{
191 std::coroutine_handle<promise_base> continuation_ = nullptr; 187 util::lazy_zstring_view target;
188 util::lazy_zstring_view data;
189};
192 190
193 public: 191struct xml_decl_event
194 // Not having this constructor marked inline messes with coroutine 192{
195 // HALO. (Hours 'wasted': many) 193 util::lazy_zstring_view version;
196 inline explicit promise_base(executor_ref executor) 194 util::lazy_zstring_view encoding;
197 : executor_{executor} 195 std::optional<bool> standalone;
198 {} 196};
199 197
200 [[nodiscard]] inline auto executor() const -> executor& { 198struct eof_event
201 return *executor_; 199{
202 } 200};
203 201
204 inline auto base_handle() -> std::coroutine_handle<promise_base> { 202using event = std::variant<
205 return std::coroutine_handle<promise_base>::from_promise(*this); 203 start_element_event, end_element_event, character_data_event,
206 } 204 processing_instructions_event, xml_decl_event, eof_event>;
207 205
208 [[nodiscard]] inline auto continuation() const -> std::coroutine_handle<promise_base> { 206template <class T>
209 return continuation_; 207concept event_type = requires(event ev) { std::get<T>(ev); };
210 }
211 inline auto set_continuation(std::coroutine_handle<promise_base> c) -> void {
212 continuation_ = c;
213 }
214 };
215 208
216 struct position { 209class executor;
217 std::size_t line; 210using executor_ref = util::not_null<executor*>;
218 std::size_t col;
219 };
220 211
221 class executor { 212struct current_event_t
222 XML_Parser p_; 213{
223 std::exception_ptr ex_ = nullptr; 214 executor_ref executor;
224 std::coroutine_handle<promise_base> continuation_ = nullptr; 215};
225 std::vector<std::optional<std::string>> default_namespace_;
226 std::unordered_map<std::string_view, std::vector<std::string>> namespaces_;
227 std::optional<event> ev_;
228 bool advance_ = true;
229 216
230 inline auto try_handle_event(event ev) noexcept -> void { 217auto current_event(executor_ref executor) -> current_event_t
231 assert(!ex_); 218{
219 return current_event_t{executor};
220}
232 221
233 try { 222class promise_base
234 ev_ = std::move(ev); 223{
235 } catch (...) { 224 executor_ref executor_;
236 ex_ = std::current_exception(); 225 std::coroutine_handle<promise_base> continuation_ = nullptr;
237 return; 226
238 } 227public:
239 advance_ = false; 228 // Not having this constructor marked inline messes with coroutine
240 if (!continuation_) { 229 // HALO. (Hours 'wasted': many)
241 // Parser returned (all subparsers are done) and has set the continuation to nullptr. 230 inline explicit promise_base(executor_ref executor) : executor_{executor} {}
242 if (auto s = XML_StopParser(p_, /* resumable */ false); s != XML_STATUS_OK) { 231
243 ex_ = std::make_exception_ptr(std::runtime_error{"unexpected error when stopping XML parser"}); 232 [[nodiscard]] inline auto executor() const -> executor& { return *executor_; }
244 return; 233
245 } 234 inline auto base_handle() -> std::coroutine_handle<promise_base>
246 ex_ = std::make_exception_ptr(std::runtime_error{"parser did not consume entire XML document"}); 235 {
236 return std::coroutine_handle<promise_base>::from_promise(*this);
237 }
238
239 [[nodiscard]] inline auto continuation() const
240 -> std::coroutine_handle<promise_base>
241 {
242 return continuation_;
243 }
244 inline auto set_continuation(std::coroutine_handle<promise_base> c) -> void
245 {
246 continuation_ = c;
247 }
248};
249
250struct position
251{
252 std::size_t line;
253 std::size_t col;
254};
255
256class executor
257{
258 XML_Parser p_;
259 std::exception_ptr ex_ = nullptr;
260 std::coroutine_handle<promise_base> continuation_ = nullptr;
261 std::vector<std::optional<std::string>> default_namespace_;
262 std::unordered_map<std::string_view, std::vector<std::string>> namespaces_;
263 std::optional<event> ev_;
264 bool advance_ = true;
265
266 inline auto try_handle_event(event ev) noexcept -> void
267 {
268 assert(!ex_);
269
270 try
271 {
272 ev_ = std::move(ev);
273 }
274 catch (...)
275 {
276 ex_ = std::current_exception();
277 return;
278 }
279 advance_ = false;
280 if (!continuation_)
281 {
282 // Parser returned (all subparsers are done) and has set the
283 // continuation to nullptr.
284 if (auto s = XML_StopParser(p_, /* resumable */ false);
285 s != XML_STATUS_OK)
286 {
287 ex_ = std::make_exception_ptr(
288 std::runtime_error{"unexpected error when stopping XML parser"});
247 return; 289 return;
248 } 290 }
249 continuation_.resume(); 291 ex_ = std::make_exception_ptr(
250 if (ex_) { 292 std::runtime_error{"parser did not consume entire XML document"});
251 // Not sure if it's useful to report this error. 293 return;
252 std::ignore = XML_StopParser(p_, /* resumable */ false); 294 }
253 } 295 continuation_.resume();
296 if (ex_)
297 {
298 // Not sure if it's useful to report this error.
299 std::ignore = XML_StopParser(p_, /* resumable */ false);
254 } 300 }
301 }
255 302
256 static auto handle_start_element(void* ctx, char const* name, char const** attrs) noexcept -> void { 303 static auto
257 auto qname = detail::split_name(name); 304 handle_start_element(void* ctx, char const* name, char const** attrs) noexcept
258 static_cast<executor*>(ctx)->try_handle_event(start_element_event{ 305 -> void
259 .name = qname, 306 {
260 .attrs = attribute_view{qname.ns_uri, attrs}, 307 auto qname = detail::split_name(name);
308 static_cast<executor*>(ctx)->try_handle_event(
309 start_element_event{
310 .name = qname,
311 .attrs = attribute_view{qname.ns_uri, attrs},
261 }); 312 });
262 } 313 }
263 static auto handle_end_element(void* ctx, char const* name) noexcept -> void { 314
264 static_cast<executor*>(ctx)->try_handle_event(end_element_event{ 315 static auto handle_end_element(void* ctx, char const* name) noexcept -> void
265 .name = detail::split_name(name), 316 {
317 static_cast<executor*>(ctx)->try_handle_event(
318 end_element_event{
319 .name = detail::split_name(name),
266 }); 320 });
267 } 321 }
268 static auto handle_character_data(void* ctx, char const* s, int len) noexcept -> void { 322
269 static_cast<executor*>(ctx)->try_handle_event(character_data_event{ 323 static auto handle_character_data(void* ctx, char const* s, int len) noexcept
270 .data = std::string_view{s, static_cast<std::size_t>(len)}, 324 -> void
325 {
326 static_cast<executor*>(ctx)->try_handle_event(
327 character_data_event{
328 .data = std::string_view{s, static_cast<std::size_t>(len)},
271 }); 329 });
272 } 330 }
273 static auto handle_processing_instructions(void* ctx, char const* target, char const* data) noexcept -> void { 331
274 static_cast<executor*>(ctx)->try_handle_event(processing_instructions_event{ 332 static auto handle_processing_instructions(
275 .target = util::lazy_zstring_view{target}, 333 void* ctx, char const* target, char const* data) noexcept -> void
276 .data = util::lazy_zstring_view{data}, 334 {
335 static_cast<executor*>(ctx)->try_handle_event(
336 processing_instructions_event{
337 .target = util::lazy_zstring_view{target},
338 .data = util::lazy_zstring_view{data},
277 }); 339 });
340 }
341
342 static auto handle_external_entity_ref(
343 XML_Parser, char const* /* context */, char const* /* base */,
344 char const* /* system_id */, char const* /* public_id */) noexcept -> int
345 {
346 return XML_STATUS_ERROR;
347 }
348
349 static auto handle_start_namespace_decl(
350 void* ctx, char const* prefix, char const* uri) noexcept -> void
351 {
352 if (prefix)
353 {
354 static_cast<executor*>(ctx)
355 ->namespaces_[std::string_view{prefix}]
356 .emplace_back(uri);
278 } 357 }
279 static auto handle_external_entity_ref(XML_Parser, char const* /* context */, char const* /* base */, char const* /* system_id */, char const* /* public_id */) noexcept -> int { 358 else
280 return XML_STATUS_ERROR; 359 {
360 static_cast<executor*>(ctx)->default_namespace_.push_back(
361 uri ? std::make_optional<std::string>(uri) : std::nullopt);
281 } 362 }
282 static auto handle_start_namespace_decl(void* ctx, char const* prefix, char const* uri) noexcept -> void { 363 }
283 if (prefix) { 364
284 static_cast<executor*>(ctx)->namespaces_[std::string_view{prefix}].emplace_back(uri); 365 static auto handle_end_namespace_decl(void* ctx, char const* prefix) noexcept
285 } else { 366 -> void
286 static_cast<executor*>(ctx)->default_namespace_.push_back(uri ? std::make_optional<std::string>(uri) : std::nullopt); 367 {
287 } 368 if (prefix)
369 {
370 static_cast<executor*>(ctx)
371 ->namespaces_[std::string_view{prefix}]
372 .pop_back();
288 } 373 }
289 static auto handle_end_namespace_decl(void* ctx, char const* prefix) noexcept -> void { 374 else
290 if (prefix) { 375 {
291 static_cast<executor*>(ctx)->namespaces_[std::string_view{prefix}].pop_back(); 376 static_cast<executor*>(ctx)->default_namespace_.pop_back();
292 } else {
293 static_cast<executor*>(ctx)->default_namespace_.pop_back();
294 }
295 } 377 }
296 static auto handle_xml_decl(void * ctx, char const* version, char const* encoding, int standalone) noexcept -> void { 378 }
297 static_cast<executor*>(ctx)->try_handle_event(xml_decl_event{ 379
298 .version = util::lazy_zstring_view{version}, 380 static auto handle_xml_decl(
299 .encoding = util::lazy_zstring_view{encoding}, 381 void* ctx, char const* version, char const* encoding,
300 .standalone = standalone < 0 ? std::nullopt : std::make_optional(standalone > 0), 382 int standalone) noexcept -> void
383 {
384 static_cast<executor*>(ctx)->try_handle_event(
385 xml_decl_event{
386 .version = util::lazy_zstring_view{version},
387 .encoding = util::lazy_zstring_view{encoding},
388 .standalone = standalone < 0 ? std::nullopt
389 : std::make_optional(standalone > 0),
301 }); 390 });
302 } 391 }
303 392
304 inline auto advance_flag() -> bool { 393 inline auto advance_flag() -> bool { return advance_; }
305 return advance_;
306 }
307 inline auto set_exception(std::exception_ptr ex) -> void {
308 ex_ = std::move(ex);
309 }
310 [[nodiscard]] inline auto take_exception() -> std::exception_ptr {
311 return std::exchange(ex_, nullptr);
312 }
313 394
314 template<class T> friend class promise; 395 inline auto set_exception(std::exception_ptr ex) -> void
396 {
397 ex_ = std::move(ex);
398 }
315 399
316 public: 400 [[nodiscard]] inline auto take_exception() -> std::exception_ptr
317 executor(); 401 {
402 return std::exchange(ex_, nullptr);
403 }
318 404
319 executor(executor const&) = delete; 405 template <class T>
320 executor(executor&&) = delete; 406 friend class promise;
321 auto operator=(executor const&) -> executor& = delete;
322 auto operator=(executor&&) -> executor& = delete;
323 407
324 ~executor(); 408public:
409 executor();
325 410
326 inline auto set_continuation(std::coroutine_handle<promise_base> c) -> void { 411 executor(executor const&) = delete;
327 continuation_ = c; 412 executor(executor&&) = delete;
328 } 413 auto operator=(executor const&) -> executor& = delete;
329 inline auto set_advance_flag() -> void { 414 auto operator=(executor&&) -> executor& = delete;
330 if (!ev_ || !std::holds_alternative<eof_event>(ev_.value())) { 415
331 advance_ = true; 416 ~executor();
332 } 417
333 } 418 inline auto set_continuation(std::coroutine_handle<promise_base> c) -> void
334 inline auto event() const -> std::optional<event> const& { 419 {
335 return ev_; 420 continuation_ = c;
336 } 421 }
337 inline auto resolve_namespace(std::string_view prefix) -> std::optional<std::string_view> { 422
338 if (auto it = namespaces_.find(prefix); it != namespaces_.end() && !it->second.empty()) 423 inline auto set_advance_flag() -> void
339 return it->second.back(); 424 {
340 return std::nullopt; 425 if (!ev_ || !std::holds_alternative<eof_event>(ev_.value()))
426 {
427 advance_ = true;
341 } 428 }
342 [[nodiscard]] inline auto position() -> position { 429 }
343 return { 430
431 inline auto event() const -> std::optional<event> const& { return ev_; }
432
433 inline auto resolve_namespace(std::string_view prefix)
434 -> std::optional<std::string_view>
435 {
436 if (auto it = namespaces_.find(prefix);
437 it != namespaces_.end() && !it->second.empty())
438 return it->second.back();
439 return std::nullopt;
440 }
441
442 [[nodiscard]] inline auto position() -> position
443 {
444 return {
344 .line = XML_GetCurrentLineNumber(p_), 445 .line = XML_GetCurrentLineNumber(p_),
345 .col = XML_GetCurrentColumnNumber(p_), 446 .col = XML_GetCurrentColumnNumber(p_),
346 }; 447 };
347 } 448 }
348 449
349 auto start() -> void; 450 auto start() -> void;
350 auto read(std::string_view xml, bool is_final) -> void; 451 auto read(std::string_view xml, bool is_final) -> void;
351 auto end() -> void; 452 auto end() -> void;
352 }; 453};
353 454
354 template<class T> 455template <class T>
355 class promise_returnable : public promise_base { 456class promise_returnable : public promise_base
356 std::optional<T> returned_value_; 457{
458 std::optional<T> returned_value_;
357 459
358 public: 460public:
359 using result_type = T; 461 using result_type = T;
360 using promise_base::promise_base; 462 using promise_base::promise_base;
361 463
362 template<class U> 464 template <class U>
363 auto return_value(U&& v) -> void { 465 auto return_value(U&& v) -> void
364 returned_value_.emplace(std::forward<U>(v)); 466 {
365 } 467 returned_value_.emplace(std::forward<U>(v));
366 auto returned_value() -> T&& { 468 }
367 if (!returned_value_)
368 throw std::runtime_error{"XML coroutine did not return"};
369 return std::forward<T>(returned_value_.value());
370 }
371 };
372 469
373 template<> 470 auto returned_value() -> T&&
374 class promise_returnable<void> : public promise_base { 471 {
375 public: 472 if (!returned_value_)
376 using result_type = void; 473 throw std::runtime_error{"XML coroutine did not return"};
377 using promise_base::promise_base; 474 return std::forward<T>(returned_value_.value());
475 }
476};
378 477
379 auto return_void() -> void {} 478template <>
380 }; 479class promise_returnable<void> : public promise_base
480{
481public:
482 using result_type = void;
483 using promise_base::promise_base;
381 484
382 template<class T> 485 auto return_void() -> void {}
383 class promise : public promise_returnable<T> { 486};
384 public:
385 // Called with all the coroutine's arguments.
386 // Ignoring all but the first argument, which should be the executor.
387 template<class... Args>
388 explicit promise(executor_ref executor, Args&&...)
389 : promise_returnable<T>{executor}
390 {}
391 487
392 auto handle() -> std::coroutine_handle<promise<T>> { 488template <class T>
393 return {parser<T>::handle_type::from_promise(*this)}; 489class promise : public promise_returnable<T>
394 } 490{
491public:
492 // Called with all the coroutine's arguments.
493 // Ignoring all but the first argument, which should be the executor.
494 template <class... Args>
495 explicit promise(executor_ref executor, Args&&...)
496 : promise_returnable<T>{executor}
497 {
498 }
395 499
396 auto get_return_object() -> parser<T> { 500 auto handle() -> std::coroutine_handle<promise<T>>
397 return parser<T>{handle()}; 501 {
398 } 502 return {parser<T>::handle_type::from_promise(*this)};
503 }
399 504
400 auto initial_suspend() { 505 auto get_return_object() -> parser<T> { return parser<T>{handle()}; }
401 return std::suspend_always{}; 506
402 } 507 auto initial_suspend() { return std::suspend_always{}; }
403 auto final_suspend() noexcept {
404 struct awaiter {
405 std::coroutine_handle<> h_;
406 508
407 [[nodiscard]] constexpr auto await_ready() const noexcept -> bool { return false; } 509 auto final_suspend() noexcept
408 auto await_suspend(std::coroutine_handle<>) -> std::coroutine_handle<> { return h_; } 510 {
409 constexpr auto await_resume() const noexcept -> void { return; } 511 struct awaiter
410 }; 512 {
411 if (this->continuation()) { 513 std::coroutine_handle<> h_;
412 return awaiter{this->continuation()}; 514
413 } else { 515 [[nodiscard]] constexpr auto await_ready() const noexcept -> bool
414 this->executor().set_continuation(nullptr); 516 {
415 return awaiter{std::noop_coroutine()}; 517 return false;
416 } 518 }
519 auto await_suspend(std::coroutine_handle<>) -> std::coroutine_handle<>
520 {
521 return h_;
522 }
523 constexpr auto await_resume() const noexcept -> void { return; }
524 };
525 if (this->continuation())
526 {
527 return awaiter{this->continuation()};
417 } 528 }
418 529 else
419 auto unhandled_exception() -> void { 530 {
420 this->executor().set_exception(std::current_exception()); 531 this->executor().set_continuation(nullptr);
532 return awaiter{std::noop_coroutine()};
421 } 533 }
534 }
422 535
423 auto await_transform(current_event_t const& req) { 536 auto unhandled_exception() -> void
424 struct awaiter { 537 {
425 executor_ref executor_; 538 this->executor().set_exception(std::current_exception());
539 }
426 540
427 [[nodiscard]] constexpr auto await_ready() const noexcept -> bool { 541 auto await_transform(current_event_t const& req)
428 return !executor_->advance_flag(); 542 {
429 } 543 struct awaiter
430 auto await_suspend(std::coroutine_handle<promise<T>> h) -> void { 544 {
431 executor_->set_continuation(h.promise().base_handle()); 545 executor_ref executor_;
432 }
433 [[nodiscard]] auto await_resume() const -> event {
434 assert(executor_->event());
435 return executor_->event().value();
436 }
437 };
438 return awaiter{req.executor};
439 }
440 546
441 template<class U> 547 [[nodiscard]] constexpr auto await_ready() const noexcept -> bool
442 auto await_transform(parser<U> const& coro) { 548 {
443 struct [[clang::coro_await_elidable]] awaiter { 549 return !executor_->advance_flag();
444 util::not_null<promise<U>*> next_; 550 }
551 auto await_suspend(std::coroutine_handle<promise<T>> h) -> void
552 {
553 executor_->set_continuation(h.promise().base_handle());
554 }
555 [[nodiscard]] auto await_resume() const -> event
556 {
557 assert(executor_->event());
558 return executor_->event().value();
559 }
560 };
561 return awaiter{req.executor};
562 }
563
564 template <class U>
565 auto await_transform(parser<U> const& coro)
566 {
567 struct [[clang::coro_await_elidable]] awaiter
568 {
569 util::not_null<promise<U>*> next_;
445 570
446 [[nodiscard]] constexpr auto await_ready() const noexcept -> bool { return false; } 571 [[nodiscard]] constexpr auto await_ready() const noexcept -> bool
447 auto await_suspend(std::coroutine_handle<promise<T>> h) -> std::coroutine_handle<> { 572 {
448 // Passed coroutine handle will be the same as parser<T>::handle_type::from_promise(*this) 573 return false;
449 next_->set_continuation(h.promise().base_handle()); 574 }
450 return next_->handle(); 575 auto await_suspend(std::coroutine_handle<promise<T>> h)
576 -> std::coroutine_handle<>
577 {
578 // Passed coroutine handle will be the same as
579 // parser<T>::handle_type::from_promise(*this)
580 next_->set_continuation(h.promise().base_handle());
581 return next_->handle();
582 }
583 auto await_resume() -> U
584 {
585 // Promise is still valid since coroutine frame is still alive
586 // (and suspended): control was transferred back to this
587 // coroutine via symmetric transfer in final_suspend(). Assuming
588 // that the destructor for coro still needs to run.
589 if (auto ex = next_->executor().take_exception())
590 {
591 std::rethrow_exception(ex);
451 } 592 }
452 auto await_resume() -> U { 593 else
453 // Promise is still valid since coroutine frame is still alive (and suspended): 594 {
454 // control was transferred back to this coroutine via symmetric transfer in 595 if constexpr (!std::is_void_v<U>)
455 // final_suspend(). Assuming that the destructor for coro still needs to run. 596 {
456 if (auto ex = next_->executor().take_exception()) { 597 return std::move(next_->returned_value());
457 std::rethrow_exception(ex);
458 } else {
459 if constexpr (!std::is_void_v<U>) {
460 return std::move(next_->returned_value());
461 }
462 } 598 }
463 } 599 }
464 }; 600 }
465 return awaiter{util::not_null{&coro.promise()}}; 601 };
466 } 602 return awaiter{util::not_null{&coro.promise()}};
467 }; 603 }
604};
468 605
469 // Helpers for handling XML documents. Non-polymorphic functions 606// Helpers for handling XML documents. Non-polymorphic functions
470 // should be marked inline to allow HALO across TU boundaries. 607// should be marked inline to allow HALO across TU boundaries.
471 608
472 template<class T> 609template <class T>
473 concept unconstrained = true; 610concept unconstrained = true;
474 611
475 template<class T, template<class U> concept C> 612template <class T, template <class U> concept C>
476 concept parser_of = requires { 613concept parser_of = requires {
477 typename T::result_type; 614 typename T::result_type;
478 requires std::same_as<parser<typename T::result_type>, T>; 615 requires std::same_as<parser<typename T::result_type>, T>;
479 requires C<typename T::result_type>; 616 requires C<typename T::result_type>;
480 }; 617};
481 618
482 template<parser_of<unconstrained> T> 619template <parser_of<unconstrained> T>
483 using parser_result_t = T::result_type; 620using parser_result_t = T::result_type;
484 621
485 template<class T, class... Args> 622template <class T, class... Args>
486 concept parser_invocable = std::invocable<T, Args...> && parser_of<std::invoke_result_t<T, Args...>, unconstrained>; 623concept parser_invocable =
624 std::invocable<T, Args...>
625 && parser_of<std::invoke_result_t<T, Args...>, unconstrained>;
487 626
488 template<class Fn, class... Args> 627template <class Fn, class... Args>
489 requires parser_invocable<Fn, Args...> 628 requires parser_invocable<Fn, Args...>
490 using parser_invoke_result_t = parser_result_t<std::invoke_result_t<Fn, Args...>>; 629using parser_invoke_result_t =
630 parser_result_t<std::invoke_result_t<Fn, Args...>>;
491 631
492 template<event_type T> auto expect_event(executor_ref e) -> parser<T> { 632template <event_type T>
493 auto ev = co_await current_event(e); 633auto expect_event(executor_ref e) -> parser<T>
494 if (!std::holds_alternative<T>(ev)) { 634{
495 auto pos = e->position(); 635 auto ev = co_await current_event(e);
496 throw std::runtime_error{std::format("at {}:{}: unexpected event type, have {}", pos.line, pos.col, ev.index())}; 636 if (!std::holds_alternative<T>(ev))
497 } 637 {
498 e->set_advance_flag(); 638 auto pos = e->position();
499 co_return std::get<T>(ev); 639 throw std::runtime_error{std::format(
640 "at {}:{}: unexpected event type, have {}", pos.line, pos.col,
641 ev.index())};
500 } 642 }
643 e->set_advance_flag();
644 co_return std::get<T>(ev);
645}
501 646
502 inline auto expect_start_element(executor_ref e, qname_view want) -> parser<attribute_view> { 647inline auto expect_start_element(executor_ref e, qname_view want)
503 auto ev = co_await expect_event<start_element_event>(e); 648 -> parser<attribute_view>
504 if (ev.name != want) { 649{
505 auto pos = e->position(); 650 auto ev = co_await expect_event<start_element_event>(e);
506 throw std::runtime_error{std::format("at {}:{}: unexpected element started", pos.line, pos.col)}; 651 if (ev.name != want)
507 } 652 {
508 co_return ev.attrs; 653 auto pos = e->position();
654 throw std::runtime_error{std::format(
655 "at {}:{}: unexpected element started", pos.line, pos.col)};
509 } 656 }
657 co_return ev.attrs;
658}
510 659
511 inline auto allow_start_element(executor_ref e, qname_view want) -> parser<std::optional<attribute_view>> { 660inline auto allow_start_element(executor_ref e, qname_view want)
512 auto ev = co_await current_event(e); 661 -> parser<std::optional<attribute_view>>
513 if (auto const* pev = std::get_if<start_element_event>(&ev)) { 662{
514 if (pev->name == want) { 663 auto ev = co_await current_event(e);
515 e->set_advance_flag(); 664 if (auto const* pev = std::get_if<start_element_event>(&ev))
516 co_return pev->attrs; 665 {
517 } 666 if (pev->name == want)
667 {
668 e->set_advance_flag();
669 co_return pev->attrs;
518 } 670 }
519 co_return std::nullopt;
520 } 671 }
672 co_return std::nullopt;
673}
521 674
522 inline auto expect_end_element(executor_ref e, qname_view want) -> parser<void> { 675inline auto expect_end_element(executor_ref e, qname_view want) -> parser<void>
523 auto ev = co_await expect_event<end_element_event>(e); 676{
524 if (ev.name != want) { 677 auto ev = co_await expect_event<end_element_event>(e);
525 throw std::runtime_error{"unexpected element ended"}; 678 if (ev.name != want)
526 } 679 {
680 throw std::runtime_error{"unexpected element ended"};
527 } 681 }
682}
528 683
529 inline auto ignore_whitespace(executor_ref e) -> parser<void> { 684inline auto ignore_whitespace(executor_ref e) -> parser<void>
530 auto all_whitespace = [](std::string_view s) -> bool { 685{
531 for (auto c : s) 686 auto all_whitespace = [](std::string_view s) -> bool
532 if (c != ' ' && c != '\r' && c != '\n' && c != '\t') 687 {
533 return false; 688 for (auto c : s)
534 return true; 689 if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
535 }; 690 return false;
691 return true;
692 };
536 693
537 while (true) { 694 while (true)
538 auto ev = co_await current_event(e); 695 {
539 if (auto const* pev = std::get_if<character_data_event>(&ev); pev && all_whitespace(pev->data)) { 696 auto ev = co_await current_event(e);
540 e->set_advance_flag(); 697 if (auto const* pev = std::get_if<character_data_event>(&ev);
541 } else { 698 pev && all_whitespace(pev->data))
542 co_return; 699 {
543 } 700 e->set_advance_flag();
701 }
702 else
703 {
704 co_return;
544 } 705 }
545 } 706 }
707}
546 708
547 auto expect_element(executor_ref e, qname_view want, parser_invocable<executor_ref, attribute_view> auto p) 709auto expect_element(
710 executor_ref e, qname_view want,
711 parser_invocable<executor_ref, attribute_view> auto p)
548 -> parser<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>> 712 -> parser<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>
713{
714 co_await ignore_whitespace(e);
715 auto attrs = co_await expect_start_element(e, want);
716 auto&& res = co_await p(e, attrs);
717 co_await expect_end_element(e, want);
718 co_await ignore_whitespace(e);
719 co_return std::forward<
720 parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>(res);
721}
722
723auto allow_element(
724 executor_ref e, qname_view want,
725 parser_invocable<executor_ref, attribute_view> auto p)
726 -> parser<std::optional<
727 parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>>
728{
729 co_await ignore_whitespace(e);
730 if (auto mattrs = co_await allow_start_element(e, want))
549 { 731 {
550 co_await ignore_whitespace(e); 732 auto&& res = co_await p(e, *mattrs);
551 auto attrs = co_await expect_start_element(e, want);
552 auto&& res = co_await p(e, attrs);
553 co_await expect_end_element(e, want); 733 co_await expect_end_element(e, want);
554 co_await ignore_whitespace(e); 734 co_await ignore_whitespace(e);
555 co_return std::forward<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>(res); 735 co_return std::make_optional(
736 std::forward<
737 parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>(
738 res));
556 } 739 }
740 co_return std::nullopt;
741}
557 742
558 auto allow_element(executor_ref e, qname_view want, parser_invocable<executor_ref, attribute_view> auto p) 743auto allow_element(
559 -> parser<std::optional<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>> 744 executor_ref e, qname_view want,
745 parser_invocable<executor_ref, attribute_view> auto p) -> parser<bool>
746 requires std::is_void_v<
747 parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>
748{
749 co_await ignore_whitespace(e);
750 if (auto mattrs = co_await allow_start_element(e, want))
560 { 751 {
752 co_await p(e, *mattrs);
753 co_await expect_end_element(e, want);
561 co_await ignore_whitespace(e); 754 co_await ignore_whitespace(e);
562 if (auto mattrs = co_await allow_start_element(e, want)) { 755 co_return true;
563 auto&& res = co_await p(e, *mattrs);
564 co_await expect_end_element(e, want);
565 co_await ignore_whitespace(e);
566 co_return std::make_optional(std::forward<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>(res));
567 }
568 co_return std::nullopt;
569 } 756 }
757 co_return false;
758}
570 759
571 auto allow_element(executor_ref e, qname_view want, parser_invocable<executor_ref, attribute_view> auto p) 760inline auto
572 -> parser<bool> 761ignore_contents(executor_ref e, std::optional<qname_view> muntil = std::nullopt)
573 requires std::is_void_v<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>> 762 -> parser<void>
763{
764 std::size_t depth = 0;
765 while (true)
574 { 766 {
575 co_await ignore_whitespace(e); 767 auto ev = co_await current_event(e);
576 if (auto mattrs = co_await allow_start_element(e, want)) { 768 if (auto* pev = std::get_if<start_element_event>(&ev))
577 co_await p(e, *mattrs); 769 {
578 co_await expect_end_element(e, want); 770 if (depth == 0 && muntil && pev->name == *muntil)
579 co_await ignore_whitespace(e); 771 {
580 co_return true; 772 co_return;
773 }
774 else
775 {
776 depth++;
777 }
581 } 778 }
582 co_return false; 779 else if (std::holds_alternative<end_element_event>(ev))
583 } 780 {
584 781 if (depth == 0)
585 inline auto ignore_contents(executor_ref e, std::optional<qname_view> muntil = std::nullopt) -> parser<void> { 782 {
586 std::size_t depth = 0; 783 co_return;
587 while (true) { 784 }
588 auto ev = co_await current_event(e); 785 else
589 if (auto* pev = std::get_if<start_element_event>(&ev)) { 786 {
590 if (depth == 0 && muntil && pev->name == *muntil) { 787 depth--;
591 co_return;
592 } else {
593 depth++;
594 }
595 } else if (std::holds_alternative<end_element_event>(ev)) {
596 if (depth == 0) {
597 co_return;
598 } else {
599 depth--;
600 }
601 } 788 }
602 e->set_advance_flag();
603 } 789 }
790 e->set_advance_flag();
604 } 791 }
605 inline auto ignore_element_contents(executor_ref e, attribute_view) -> parser<void> { 792}
606 co_await ignore_contents(e); 793inline auto ignore_element_contents(executor_ref e, attribute_view)
607 } 794 -> parser<void>
795{
796 co_await ignore_contents(e);
797}
608 798
609 inline auto read_string(executor_ref e) -> parser<std::string> { 799inline auto read_string(executor_ref e) -> parser<std::string>
610 std::string s; 800{
611 while (true) { 801 std::string s;
612 auto ev = co_await current_event(e); 802 while (true)
613 if (auto* pev = std::get_if<character_data_event>(&ev)) { 803 {
614 e->set_advance_flag(); 804 auto ev = co_await current_event(e);
615 s += pev->data; 805 if (auto* pev = std::get_if<character_data_event>(&ev))
616 } else { 806 {
617 co_return s; 807 e->set_advance_flag();
618 } 808 s += pev->data;
809 }
810 else
811 {
812 co_return s;
619 } 813 }
620 } 814 }
621 inline auto read_string_contents(executor_ref e, attribute_view) -> parser<std::string> { 815}
622 co_return co_await read_string(e); 816inline auto read_string_contents(executor_ref e, attribute_view)
623 } 817 -> parser<std::string>
818{
819 co_return co_await read_string(e);
820}
624 821
625 template<auto f> 822template <auto f>
626 auto hohalo() { 823auto hohalo()
627 return []<class... Args>(Args&&... args) -> std::invoke_result_t<decltype(f), Args...> { 824{
628 co_return co_await f(std::forward<Args>(args)...); 825 return []<class... Args>(
629 }; 826 Args&&... args) -> std::invoke_result_t<decltype(f), Args...>
630 } 827 { co_return co_await f(std::forward<Args>(args)...); };
828}
631 829
632} // namespace routemon::xml 830} // namespace routemon::xml