Clingo
Loading...
Searching...
No Matches
string.hh
1#pragma once
2
3#include <cstdint>
4#include <stdexcept>
5#include <string_view>
6
7namespace CppClingo::Util {
8
9namespace Detail {
10
11// NOLINTBEGIN(readability-magic-numbers)
12
13inline auto hex_val(char c) -> uint32_t {
14 if (c >= '0' && c <= '9') {
15 return c - '0';
16 }
17 if (c >= 'A' && c <= 'F') {
18 return 10 + (c - 'A');
19 }
20 if (c >= 'a' && c <= 'f') {
21 return 10 + (c - 'a');
22 }
23 throw std::invalid_argument("invalid hex character");
24}
25
26inline auto encode_utf8(uint32_t cp, auto out) -> void {
27 if (cp > 0x10FFFF) {
28 throw std::invalid_argument("invalid unicode code point");
29 }
30 if (cp <= 0x7F) {
31 *out++ = static_cast<char>(cp);
32 } else if (cp <= 0x7FF) {
33 *out++ = static_cast<char>(0xC0 | (cp >> 6));
34 *out++ = static_cast<char>(0x80 | (cp & 0x3F));
35 } else if (cp <= 0xFFFF) {
36 *out++ = static_cast<char>(0xE0 | (cp >> 12));
37 *out++ = static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
38 *out++ = static_cast<char>(0x80 | (cp & 0x3F));
39 } else {
40 *out++ = static_cast<char>(0xF0 | (cp >> 18));
41 *out++ = static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
42 *out++ = static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
43 *out++ = static_cast<char>(0x80 | (cp & 0x3F));
44 }
45}
46
47inline auto parse_unicode_escape(auto it, auto ie, auto out) -> auto {
48 uint32_t cp = 0;
49 if (it == ie || *it != '{') {
50 throw std::runtime_error("expected '{' at the beginning of unicode escape");
51 }
52 size_t count = 0;
53 for (++it; it != ie; ++it, ++count) {
54 if (*it == '}') {
55 if (count == 0) {
56 throw std::runtime_error("expected at least one hex digit in unicode escape");
57 }
58 encode_utf8(cp, out);
59 return it;
60 }
61 if (count >= 6) {
62 throw std::runtime_error("too many hex digits in unicode escape");
63 }
64 cp = (cp << 4) | hex_val(*it);
65 }
66 throw std::runtime_error("expected '}' at the end of unicode escape");
67}
68
69// NOLINTEND(readability-magic-numbers)
70
71} // namespace Detail
72
73void unquote(std::string_view in, auto out, bool fstring = false) {
74 auto escape = '\0';
75 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
76 for (auto it = in.begin(), ie = in.end(); it != ie; ++it) {
77 char c = *it;
78 if (escape == '{' || escape == '}') {
79 if (c == escape) {
80 *out++ = escape;
81 } else {
82 throw std::runtime_error("expected brace");
83 }
84 escape = '\0';
85 } else if (escape == '\\') {
86 switch (c) {
87 case 'u': {
88 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
89 it = Detail::parse_unicode_escape(it + 1, ie, out);
90 break;
91 }
92 case 'n': {
93 *out++ = '\n';
94 break;
95 }
96 case 't': {
97 *out++ = '\t';
98 break;
99 }
100 case 'r': {
101 *out++ = '\r';
102 break;
103 }
104 case '\\': {
105 *out++ = '\\';
106 break;
107 }
108 case '"': {
109 *out++ = '"';
110 break;
111 }
112 default: {
113 throw std::runtime_error("invalid escape sequence");
114 }
115 }
116 escape = '\0';
117 } else if (c == '\\' || (fstring && (c == '{' || c == '}'))) {
118 escape = c;
119 } else {
120 *out++ = c;
121 }
122 }
123}
124
125} // namespace CppClingo::Util