@@ -65,6 +65,15 @@ struct rgb_t {
65
65
rgb_type type{rgb_type::foreground};
66
66
};
67
67
68
+ enum class op_type : std::uint8_t { open, close };
69
+ template <typename T>
70
+ struct attr_op_t {
71
+ T t{};
72
+ op_type op{};
73
+ };
74
+
75
+ enum class attr_type : std::uint8_t { style, rgb };
76
+
68
77
constexpr std::uint8_t make_rgb (float r, float g, float b) {
69
78
auto const ir = static_cast <int >(r * 5 .0f );
70
79
auto const ig = static_cast <int >(g * 5 .0f );
@@ -88,6 +97,9 @@ constexpr float clamp(float const a, float const lo, float const hi) {
88
97
return a;
89
98
}
90
99
100
+ constexpr bool is_foreground (std::string_view str) { return str == " rgb" ; }
101
+ constexpr bool is_background (std::string_view str) { return str == " bg" || str == " background" ; }
102
+
91
103
constexpr bool to_rgb (rgb_t & out, std::string_view const str) {
92
104
if (str.size () != 3 ) { return false ; }
93
105
auto const r = clamp (static_cast <float >(str[0 ] - ' 0' ) / 5 .0f , 0 .0f , 1 .0f );
@@ -97,30 +109,46 @@ constexpr bool to_rgb(rgb_t& out, std::string_view const str) {
97
109
return true ;
98
110
}
99
111
100
- enum class token_type : std:: uint8_t { text, style, rgb };
101
- enum class op_type : std::uint8_t { open, close };
112
+ struct tokenizer_t {
113
+ std::string_view text{ };
102
114
103
- template <typename T>
104
- struct attribute_t {
105
- T t{};
106
- op_type op{};
107
- };
115
+ constexpr std::pair<std::size_t , std::size_t > find_attribute () const {
116
+ if (auto const open = text.find (' <' ); open != std::string_view::npos) { return {open, text.find (' >' , open + 1 )}; }
117
+ return {std::string_view::npos, std::string_view::npos};
118
+ }
108
119
109
- struct token_t {
110
- std::string_view text{};
111
- style_t style{};
112
- rgb_t rgb{};
113
- token_type type{};
114
- op_type op{};
115
- };
120
+ constexpr bool try_attribute (std::string_view& out, std::size_t const close) {
121
+ if (close != std::string_view::npos) {
122
+ out = text.substr (0 , close + 1 );
123
+ text = text.substr (close + 1 );
124
+ return true ;
125
+ }
126
+ return false ;
127
+ }
116
128
117
- struct scanner_t {
118
- struct assign_t {
119
- std::string_view lhs{};
120
- std::string_view rhs{};
121
- };
129
+ constexpr bool operator ()(std::string_view& out) {
130
+ if (text.empty ()) { return false ; }
131
+ auto const [open, close] = find_attribute ();
122
132
123
- std::string_view text{};
133
+ if (open == 0 && close != std::string_view::npos) {
134
+ if (try_attribute (out, close)) { return true ; }
135
+ }
136
+
137
+ if (close != std::string_view::npos) {
138
+ out = text.substr (0 , open);
139
+ text = text.substr (open);
140
+ } else {
141
+ out = text;
142
+ text = {};
143
+ }
144
+
145
+ return true ;
146
+ }
147
+ };
148
+
149
+ struct key_value_t {
150
+ std::string_view key{};
151
+ std::string_view value{};
124
152
125
153
static constexpr bool is_space (char const ch) { return ch == ' ' || ch == ' \t ' || ch == ' \n ' ; }
126
154
@@ -130,105 +158,68 @@ struct scanner_t {
130
158
return in;
131
159
}
132
160
133
- static constexpr bool is_foreground (std::string_view str) { return str == " rgb" ; }
134
- static constexpr bool is_background (std::string_view str) { return str == " bg" || str == " background" ; }
135
-
136
- static constexpr assign_t assignment (std::string_view const str) {
161
+ static constexpr key_value_t make (std::string_view const str) {
137
162
if (auto eq = str.find (' =' ); eq != std::string_view::npos) { return {trim (str.substr (0 , eq)), trim (str.substr (eq + 1 ))}; }
138
163
return {str};
139
164
}
165
+ };
140
166
141
- constexpr std::string_view attribute_str () {
142
- auto const close = text.find (' >' );
143
- auto ret = std::string_view{};
144
- if (close == std::string_view::npos) {
145
- text = {};
146
- ret = {};
147
- } else {
148
- ret = text.substr (1 , close - 1 );
149
- text = text.substr (close + 1 );
150
- }
151
- return ret;
152
- }
167
+ struct attribute_t {
168
+ style_t style{};
169
+ rgb_t rgb{};
170
+ attr_type type{};
171
+ op_type op{};
153
172
154
- constexpr bool make_closing ( token_t & out_token, std::string_view const str ) {
155
- if (out_token. op != op_type::close) { return false ; }
156
- if (is_background (str )) {
157
- out_token. type = token_type ::rgb;
158
- out_token. rgb .type = rgb_type::background;
173
+ constexpr bool make_close ( key_value_t const & kvp ) {
174
+ if (op != op_type::close) { return false ; }
175
+ if (is_background (kvp. key )) {
176
+ type = attr_type ::rgb;
177
+ rgb.type = rgb_type::background;
159
178
return true ;
160
179
}
161
- if (is_foreground (str )) {
162
- out_token. type = token_type ::rgb;
163
- out_token. rgb .type = rgb_type::foreground;
180
+ if (is_foreground (kvp. key )) {
181
+ type = attr_type ::rgb;
182
+ rgb.type = rgb_type::foreground;
164
183
return true ;
165
184
}
166
185
return false ;
167
186
}
168
187
169
- constexpr bool make_assign ( token_t & out_token, assign_t const assign ) {
170
- if (out_token. op != op_type::open) { return false ; }
171
- if (is_background (assign. lhs ) && to_rgb (out_token. rgb , assign. rhs )) {
172
- out_token. type = token_type ::rgb;
173
- out_token. rgb .type = rgb_type::background;
188
+ constexpr bool make_key_value ( key_value_t const & kvp ) {
189
+ if (op != op_type::open) { return false ; }
190
+ if (is_background (kvp. key ) && to_rgb (rgb, kvp. value )) {
191
+ type = attr_type ::rgb;
192
+ rgb.type = rgb_type::background;
174
193
return true ;
175
194
}
176
- if (is_foreground (assign. lhs ) && to_rgb (out_token. rgb , assign. rhs )) {
177
- out_token. type = token_type ::rgb;
178
- out_token. rgb .type = rgb_type::foreground;
195
+ if (is_foreground (kvp. key ) && to_rgb (rgb, kvp. value )) {
196
+ type = attr_type ::rgb;
197
+ rgb.type = rgb_type::foreground;
179
198
return true ;
180
199
}
181
200
return false ;
182
201
}
183
202
184
- constexpr bool make_attribute (token_t & out_token, std::string_view const str) {
203
+ constexpr bool make_attribute (std::string_view const str) {
185
204
// try style
186
- if (to_style (out_token. style , str)) {
187
- out_token. type = token_type ::style;
205
+ if (to_style (style, str)) {
206
+ type = attr_type ::style;
188
207
return true ;
189
208
}
190
209
191
210
// try assign
192
- auto const assign = assignment (str);
193
- if (assign.rhs .empty ()) { return make_closing (out_token, assign.lhs ); }
194
- return make_assign (out_token, assign);
211
+ auto const kvp = key_value_t::make (str);
212
+ return kvp.value .empty () ? make_close (kvp) : make_key_value (kvp);
195
213
}
196
214
197
- constexpr bool attribute ( token_t & out_token, std::string_view str) {
198
- out_token. op = op_type::open;
215
+ constexpr bool try_make ( std::string_view str) {
216
+ op = op_type::open;
199
217
if (!str.empty () && str.front () == ' /' ) {
200
- out_token. op = op_type::close;
218
+ op = op_type::close;
201
219
str = str.substr (1 );
202
220
}
203
221
if (str.empty ()) { return false ; }
204
- return make_attribute (out_token, str);
205
- }
206
-
207
- constexpr bool next (token_t & out_token) {
208
- if (text.empty ()) { return false ; }
209
- out_token.text = {};
210
- out_token.type = token_type::text;
211
- auto const open = text.find (' <' );
212
- if (open == std::string_view::npos) {
213
- // no attributes in text
214
- out_token.text = text;
215
- text = {};
216
- return true ;
217
- }
218
-
219
- if (open > 0 ) {
220
- // remaining text before attribute
221
- out_token.text = text.substr (0 , open);
222
- text = text.substr (open);
223
- return true ;
224
- }
225
-
226
- auto str = attribute_str ();
227
- if (str.empty ()) { return false ; }
228
- if (attribute (out_token, str)) { return true ; }
229
-
230
- // skip
231
- return next (out_token);
222
+ return make_attribute (str);
232
223
}
233
224
};
234
225
@@ -276,7 +267,7 @@ struct pen_t {
276
267
return *this ;
277
268
}
278
269
279
- constexpr pen_t & write (attribute_t <rgb_t > rgb) {
270
+ constexpr pen_t & write (attr_op_t <rgb_t > rgb) {
280
271
write (attribute_start_v);
281
272
if (rgb.t .type == rgb_type::foreground) {
282
273
if (rgb.op == op_type::close) { return write (" 39m" ); }
@@ -289,7 +280,7 @@ struct pen_t {
289
280
return write (" m" );
290
281
}
291
282
292
- constexpr pen_t & write (attribute_t <style_t > style) {
283
+ constexpr pen_t & write (attr_op_t <style_t > style) {
293
284
if (style.t == style_t ::clear) { write ({style_t ::reset}); }
294
285
write (attribute_start_v);
295
286
if (style.op == op_type::close) {
@@ -310,17 +301,22 @@ struct null_writer {
310
301
311
302
template <typename Out>
312
303
constexpr pen_t <Out>& write (pen_t <Out>& out_pen, std::string_view const text, std::size_t capacity = std::string_view::npos) {
313
- auto scanner = scanner_t {text};
314
- auto token = token_t {};
315
304
auto vacant = [capacity, &out_pen] { return capacity == std::string_view::npos || out_pen.written < capacity; };
316
- while (scanner.next (token) && vacant ()) {
317
- switch (token.type ) {
318
- case token_type::rgb: out_pen.write ({token.rgb , token.op }); break ;
319
- case token_type::style: out_pen.write ({token.style , token.op }); break ;
320
- default :
321
- case token_type::text: out_pen.write (token.text ); break ;
305
+ auto write_token = [&out_pen](std::string_view const token) -> pen_t <Out>& {
306
+ if (token.front () == ' <' && token.back () == ' >' ) {
307
+ auto attr = attribute_t {};
308
+ if (attr.try_make (token.substr (1 , token.size () - 2 ))) {
309
+ switch (attr.type ) {
310
+ case attr_type::rgb: return out_pen.write ({attr.rgb , attr.op });
311
+ default : return out_pen.write ({attr.style , attr.op });
312
+ }
313
+ }
322
314
}
323
- }
315
+ return out_pen.write (token);
316
+ };
317
+ auto tokenizer = tokenizer_t {text};
318
+ auto token = std::string_view{};
319
+ while (vacant () && tokenizer (token)) { write_token (token); }
324
320
return out_pen;
325
321
}
326
322
} // namespace detail
0 commit comments