GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 313, 0 excluded
0.0%
Functions:
0 of 65, 0 excluded
0.0%
Branches:
0 of 165, 0 excluded
0.0%

libs/kdl/src/eu/kdl/kdl.cc
Line Branch Exec Source
1 #include "eu/kdl/kdl.h"
2
3 #include <cassert>
4 #include <kdl/kdl.h>
5
6 namespace eu::kdl
7 {
8
9
10 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 // internal helper functions
12 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
13
14 namespace
15 {
16 ✗ std::string_view to_u8string_view(kdl_str const& s)
17 {
18 ✗ return std::string_view{reinterpret_cast<char const*>(s.data), s.len};
19 }
20
21 ✗ kdl_str to_kdl_str(std::string_view s)
22 {
23 ✗ return kdl_str{.data = (s.data()), .len = s.size()};
24 }
25
26 ✗ std::variant<integer_type, float_type, std::string> kdl_number_to_variant(kdl_number const& n)
27 {
28 ✗ switch (n.type) {
29 ✗ case KDL_NUMBER_TYPE_INTEGER:
30 ✗ return n.integer;
31 ✗ case KDL_NUMBER_TYPE_FLOATING_POINT:
32 ✗ return n.floating_point;
33 ✗ case KDL_NUMBER_TYPE_STRING_ENCODED:
34 ✗ return std::string{to_u8string_view(n.string)};
35 ✗ default:
36 ✗ assert(false && "invalid kdl_number");
37 return 0;
38 }
39 }
40
41 ✗ std::variant<std::monostate, bool, Number, std::string> kdl_value_to_variant(kdl_value const& val)
42 {
43 ✗ switch (val.type) {
44 ✗ case KDL_TYPE_NULL:
45 ✗ return std::monostate{};
46 ✗ case KDL_TYPE_BOOLEAN:
47 ✗ return val.boolean;
48 ✗ case KDL_TYPE_NUMBER:
49 ✗ return Number{val.number};
50 ✗ case KDL_TYPE_STRING:
51 ✗ return std::string{to_u8string_view(val.string)};
52 ✗ default:
53 ✗ assert(false && "invalid kdl_value");
54 return std::monostate{};
55 }
56 }
57
58 [[nodiscard]]
59 ✗ bool emit_nodes(kdl_emitter* emitter, std::vector<Node> const& nodes)
60 {
61 ✗ for (const auto& node : nodes)
62 {
63 ✗ if (node.type_annotation.has_value())
64 {
65 ✗ if (!kdl_emit_node_with_type(
66 ✗ emitter, to_kdl_str(*node.type_annotation), to_kdl_str(node.name)))
67 {
68 ✗ return false;
69 }
70 } else {
71 ✗ if (!kdl_emit_node(emitter, to_kdl_str(node.name)))
72 {
73 ✗ return false;
74 }
75 }
76
77 ✗ for (const auto& arg : node.args)
78 {
79 ✗ auto v = static_cast<kdl_value>(arg);
80 ✗ if (!kdl_emit_arg(emitter, &v))
81 {
82 ✗ return false;
83 }
84 }
85
86 ✗ for (const auto& [key, value] : node.properties)
87 {
88 ✗ auto v = static_cast<kdl_value>(value);
89 ✗ if (!kdl_emit_property(emitter, to_kdl_str(key), &v))
90 {
91 ✗ return false;
92 }
93 }
94
95 ✗ if (!node.children.empty())
96 {
97 ✗ if (!kdl_start_emitting_children(emitter))
98 {
99 ✗ return false;
100 }
101 ✗ if (emit_nodes(emitter, node.children) == false)
102 {
103 ✗ return false;
104 }
105 ✗ if (!kdl_finish_emitting_children(emitter))
106 {
107 ✗ return false;
108 }
109 }
110 }
111
112 ✗ return true;
113 }
114
115 template<typename TDefer>
116 struct Defer
117 {
118 TDefer t;
119
120 ✗ explicit Defer(TDefer&& a)
121 ✗ : t(std::move(a))
122 ✗ {}
123
124 ✗ ~Defer()
125 {
126 ✗ t();
127 ✗ }
128
129 Defer() = delete;
130
131 Defer(const Defer&) = delete;
132 Defer(Defer&&) = delete;
133
134 Defer operator=(const Defer&) = delete;
135 Defer operator=(Defer&&) = delete;
136 };
137 } // namespace
138
139
140
141 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
142 // Number
143 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
144
145
146 ✗ Number::Number()
147 ✗ : m_value{ 0ll }
148 ✗ {}
149
150 ✗ Number::Number(long long n)
151 ✗ : m_value{ n }
152 ✗ {}
153
154 ✗ Number::Number(long n)
155 ✗ : m_value{ static_cast<integer_type>(n) }
156 ✗ {}
157
158 ✗ Number::Number(int n)
159 ✗ : m_value{ static_cast<integer_type>(n) }
160 ✗ {}
161
162 ✗ Number::Number(short n)
163 ✗ : m_value{ static_cast<integer_type>(n) }
164 ✗ {}
165
166 ✗ Number::Number(double n)
167 ✗ : m_value{ n }
168 ✗ {}
169
170 ✗ Number::Number(float n)
171 ✗ : m_value{ static_cast<float_type>(n) }
172 ✗ {}
173
174 ✗ Number::Number(kdl_number const& n)
175 ✗ : m_value{ kdl_number_to_variant(n) }
176 ✗ {}
177
178 ✗ NumberType Number::type() const noexcept
179 {
180 ✗ return static_cast<NumberType>(m_value.index());
181 }
182
183 ✗ Number::operator kdl_number() const
184 {
185 kdl_number result;
186 ✗ std::visit(
187 ✗ [&result]<typename T0>(const T0& n) {
188 using T = std::decay_t<T0>;
189 if constexpr (std::is_same_v<T, integer_type>) {
190 ✗ result.type = KDL_NUMBER_TYPE_INTEGER;
191 ✗ result.integer = n;
192 } else if constexpr (std::is_same_v<T, float_type>) {
193 ✗ result.type = KDL_NUMBER_TYPE_FLOATING_POINT;
194 ✗ result.floating_point = n;
195 } else if constexpr (std::is_same_v<T, std::string>) {
196 ✗ result.type = KDL_NUMBER_TYPE_STRING_ENCODED;
197 ✗ result.string = to_kdl_str(n);
198 } else {
199 assert(false && "incomplete visit");
200 result.type = KDL_NUMBER_TYPE_INTEGER;
201 result.integer = 0;
202 }
203 ✗ },
204 ✗ m_value);
205 ✗ return result;
206 }
207
208
209 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
210 // HasTypeAnnotation
211 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
212
213 ✗ HasTypeAnnotation::HasTypeAnnotation(std::string_view t)
214 ✗ : type_annotation{ t }
215 ✗ {}
216
217 ✗ void HasTypeAnnotation::remove_type_annotation()
218 {
219 ✗ type_annotation.reset();
220 ✗ }
221
222
223 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
224 // Value
225 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
226
227
228 ✗ Value::Value(bool b)
229 ✗ : m_value{ b }
230 ✗ {}
231
232 ✗ Value::Value(std::string_view s)
233 ✗ : m_value{ std::string{s} }
234 ✗ {}
235
236 ✗ Value::Value(std::string s)
237 ✗ : m_value{ std::move(s) }
238 ✗ {}
239
240 ✗ Value::Value(char const* s)
241 ✗ : m_value{ std::string{s} }
242 ✗ {}
243
244 ✗ Value::Value(Number n)
245 ✗ : m_value{ std::move(n) }
246 ✗ {}
247
248 ✗ Value::Value(std::string_view ta, bool b)
249 : HasTypeAnnotation{ ta }
250 ✗ , m_value{ b }
251 ✗ {}
252
253 ✗ Value::Value(std::string_view ta, std::string_view s)
254 : HasTypeAnnotation{ ta }
255 ✗ , m_value{ std::string{s} }
256 ✗ {}
257
258 ✗ Value::Value(std::string_view ta, std::string s)
259 : HasTypeAnnotation{ ta },
260 ✗ m_value{ std::move(s) }
261 ✗ {}
262
263 ✗ Value::Value(std::string_view ta, Number n)
264 : HasTypeAnnotation{ ta },
265 ✗ m_value{ std::move(n) }
266 ✗ {}
267
268 ✗ Value::Value(kdl_value const& val)
269 ✗ : m_value(kdl_value_to_variant(val))
270 {
271 ✗ if (val.type_annotation.data != nullptr) {
272 ✗ type_annotation = to_u8string_view(val.type_annotation);
273 }
274 ✗ }
275
276 ✗ Value& Value::operator=(bool b)
277 {
278 ✗ m_value = b;
279 ✗ return *this;
280 }
281
282 ✗ Value& Value::operator=(std::string_view s)
283 {
284 ✗ m_value = std::string{ s };
285 ✗ return *this;
286 }
287
288 ✗ Value& Value::operator=(std::string s)
289 {
290 ✗ m_value = std::move(s);
291 ✗ return *this;
292 }
293
294 ✗ Value& Value::operator=(Number const& n)
295 {
296 ✗ m_value = n;
297 ✗ return *this;
298 }
299
300 ✗ Value& Value::operator=(Number&& n)
301 {
302 ✗ m_value = std::move(n);
303 ✗ return *this;
304 }
305
306 ✗ void Value::set_to_null()
307 {
308 ✗ m_value = std::monostate{};
309 ✗ }
310
311 ✗ Type Value::type() const noexcept
312 {
313 ✗ return static_cast<Type>(m_value.index());
314 }
315
316 ✗ const Number& Value::as_number() const
317 {
318 ✗ if (std::holds_alternative<Number>(m_value)) {
319 ✗ return std::get<Number>(m_value);
320 }
321 else {
322 ✗ static const Number missing_number = 0;
323 ✗ return missing_number;
324 }
325 }
326
327 ✗ const std::string& Value::as_string() const
328 {
329 ✗ if (std::holds_alternative<std::string>(m_value)) {
330 ✗ return std::get<std::string>(m_value);
331 }
332 else {
333 ✗ static const std::string missing_string = "";
334 ✗ return missing_string;
335 }
336 }
337
338 ✗ bool Value::as_bool() const
339 {
340 ✗ if (std::holds_alternative<bool>(m_value)) {
341 ✗ return std::get<bool>(m_value);
342 }
343 else {
344 ✗ return false;
345 }
346 }
347
348 ✗ bool Value::is_null() const
349 {
350 ✗ return std::holds_alternative<std::monostate>(m_value);
351 }
352
353 ✗ Value::operator kdl_value() const
354 {
355 kdl_value result;
356
357 ✗ std::visit([&result](const auto& v)
358 {
359 using T = std::decay_t<decltype(v)>;
360 if constexpr (std::is_same_v<T, bool>)
361 {
362 ✗ result.type = KDL_TYPE_BOOLEAN;
363 ✗ result.boolean = v;
364 } else if constexpr (std::is_same_v<T, Number>)
365 {
366 ✗ result.type = KDL_TYPE_NUMBER;
367 ✗ result.number = static_cast<kdl_number>(v);
368 } else if constexpr (std::is_same_v<T, std::string>)
369 {
370 ✗ result.type = KDL_TYPE_STRING;
371 ✗ result.string = to_kdl_str(v);
372 } else {
373 ✗ result.type = KDL_TYPE_NULL;
374 }
375 ✗ },
376 ✗ m_value);
377
378 ✗ if (type_annotation.has_value())
379 {
380 ✗ result.type_annotation = to_kdl_str(*type_annotation);
381 }
382 else
383 {
384 ✗ result.type_annotation = {.data = nullptr, .len = 0};
385 }
386
387 ✗ return result;
388 }
389
390
391 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
392 // Node
393 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
394
395
396 ✗ Node::Node(std::string_view name)
397 ✗ : name{ name }
398 ✗ {}
399
400 ✗ Node::Node(std::string_view ta, std::string_view name)
401 : HasTypeAnnotation{ ta }
402 ✗ , name{ name }
403 ✗ {}
404
405 ✗ Node::Node(std::string_view name, std::vector<Value> args, std::map<std::string, Value, std::less<>> properties, std::vector<Node> children)
406 ✗ : name{ name }
407 ✗ , args{ std::move(args) }
408 ✗ , properties{ std::move(properties) }
409 ✗ , children{ std::move(children) }
410 ✗ {}
411
412 ✗ Node::Node(std::string_view ta, std::string_view name, std::vector<Value> args, std::map<std::string, Value, std::less<>> properties, std::vector<Node> children)
413 : HasTypeAnnotation{ ta }
414 ✗ , name{ name }
415 ✗ , args{ std::move(args) }
416 ✗ , properties{ std::move(properties) }
417 ✗ , children{ std::move(children) }
418 ✗ {}
419
420
421
422 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
423 // Document
424 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
425
426 ✗ Document::Document(std::vector<Node> nodes)
427 ✗ : nodes{ std::move(nodes) }
428 ✗ {}
429
430 ✗ Document::Document(std::initializer_list<Node> nodes)
431 ✗ : nodes{ nodes }
432 ✗ {}
433
434 ✗ std::optional<std::string> Document::to_string() const
435 {
436 ✗ return to_string(Version::kdl_1);
437 }
438
439 ✗ std::optional<std::string> Document::to_string(Version version) const
440 {
441 ✗ kdl_emitter_options opts = KDL_DEFAULT_EMITTER_OPTIONS;
442
443 ✗ if (version == Version::kdl_1) opts.version = KDL_VERSION_1;
444 ✗ if (version == Version::kdl_2) opts.version = KDL_VERSION_2;
445
446 ✗ kdl_emitter* emitter = kdl_create_buffering_emitter(&opts);
447 ✗ if (emitter == nullptr)
448 {
449 ✗ assert(false && "Error initializing the KDL emitter");
450 return std::nullopt;
451 }
452 ✗ auto defer_emitter = Defer{[&]{ kdl_destroy_emitter(emitter); }};
453
454 ✗ if (false == emit_nodes(emitter, nodes))
455 {
456 ✗ return std::nullopt;
457 }
458
459 ✗ auto result = std::string{to_u8string_view(kdl_get_emitter_buffer(emitter))};
460 ✗ return result;
461 ✗ }
462
463 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
464 // DocumentResult
465 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
466
467
468 ✗ DocumentResult::DocumentResult(Document doc)
469 ✗ : document{ std::move(doc) }
470 ✗ {}
471
472 ✗ DocumentResult::DocumentResult(std::string err)
473 ✗ : error{ std::move(err) }
474 ✗ {}
475
476
477 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
478 // Parsing
479 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
480
481 namespace
482 {
483
484 ✗ DocumentResult Document_read_from(kdl_parser* parser)
485 {
486 ✗ Document doc;
487 ✗ auto* node_list = &doc.nodes;
488 ✗ Node* current_node = nullptr;
489 ✗ std::vector<Node*> stack;
490
491 while (true)
492 {
493 ✗ auto* ev = kdl_parser_next_event(parser);
494
495 ✗ switch (ev->event)
496 {
497 ✗ case KDL_EVENT_EOF:
498 ✗ return DocumentResult{ doc };
499 ✗ case KDL_EVENT_PARSE_ERROR:
500 ✗ return DocumentResult{ static_cast<std::string>(to_u8string_view(ev->value.string)) };
501 ✗ case KDL_EVENT_START_NODE:
502 {
503 ✗ auto name = to_u8string_view(ev->name);
504 ✗ if (ev->value.type_annotation.data != nullptr)
505 {
506 ✗ auto ta = to_u8string_view(ev->value.type_annotation);
507 ✗ current_node = &node_list->emplace_back(ta, name);
508 }
509 else
510 {
511 ✗ current_node = &node_list->emplace_back(name);
512 }
513 ✗ node_list = &current_node->children;
514 ✗ stack.push_back(current_node);
515 ✗ break;
516 }
517 ✗ case KDL_EVENT_END_NODE:
518 ✗ stack.pop_back();
519 ✗ if (stack.empty())
520 {
521 ✗ current_node = nullptr;
522 ✗ node_list = &doc.nodes;
523 }
524 else
525 {
526 ✗ current_node = stack.back();
527 ✗ node_list = &current_node->children;
528 }
529 ✗ break;
530 ✗ case KDL_EVENT_ARGUMENT:
531 ✗ current_node->args.emplace_back(ev->value);
532 ✗ break;
533 ✗ case KDL_EVENT_PROPERTY:
534 ✗ current_node->properties[std::string{ to_u8string_view(ev->name) }] = Value{ ev->value };
535 ✗ break;
536 ✗ case KDL_EVENT_COMMENT:
537 // ignore comments
538 ✗ break;
539 ✗ default:
540 ✗ assert(false && "Invalid event from kdl_parser");
541 break;
542 }
543 ✗ }
544 ✗ }
545 }
546
547
548 ✗ DocumentResult parse(std::string_view kdl_text)
549 {
550 ✗ return parse(kdl_text, Version::any);
551 }
552
553 ✗ DocumentResult parse(std::string_view kdl_text, Version version)
554 {
555 ✗ kdl_parse_option opts = KDL_DEFAULTS;
556
557 ✗ switch (version)
558 {
559 ✗ case Version::kdl_1:
560 ✗ opts = KDL_READ_VERSION_1;
561 ✗ break;
562 ✗ case Version::kdl_2:
563 ✗ opts = KDL_READ_VERSION_2;
564 ✗ break;
565 ✗ case Version::any:
566 {
567 ✗ auto ret = parse(kdl_text, Version::kdl_2);
568 ✗ if (ret.document.has_value())
569 {
570 ✗ return ret;
571 }
572 else
573 {
574 ✗ return parse(kdl_text, Version::kdl_1);
575 }
576 ✗ }
577
578 ✗ default:
579 ✗ assert(false && "unhandled case");
580 break;
581 }
582
583 ✗ const kdl_str text = {.data = (kdl_text.data()), .len = kdl_text.size()};
584 ✗ kdl_parser* parser = kdl_create_string_parser(text, opts);
585 ✗ if (parser == nullptr)
586 {
587 ✗ assert(false && "Error initializing the KDL parser");
588 return DocumentResult("internal error");
589 }
590 ✗ auto defer_parser = Defer{[&]{ kdl_destroy_parser(parser); }};
591
592 ✗ auto doc = Document_read_from(parser);
593 ✗ return doc;
594 ✗ }
595
596 }
597