Terminal++ 3.1.0.4
A C++ library for interacting with ANSI terminal windows
Loading...
Searching...
No Matches
element_udl.hpp
1#include "terminalpp/ansi/charset.hpp"
2#include "terminalpp/character_set.hpp"
3
4#include <cstdint>
5
6namespace terminalpp::detail {
7
8enum class parser_state
9{
10 idle,
11 escape,
12 charcode_0,
13 charcode_1,
14 charcode_2,
15 charset,
16 charset_ext,
17 intensity,
18 polarity,
19 underlining,
20 fg_low_colour,
21 fg_high_colour_0,
22 fg_high_colour_1,
23 fg_high_colour_2,
24 fg_greyscale_colour_0,
25 fg_greyscale_colour_1,
26 fg_true_colour_0,
27 fg_true_colour_1,
28 fg_true_colour_2,
29 fg_true_colour_3,
30 fg_true_colour_4,
31 fg_true_colour_5,
32 bg_low_colour,
33 bg_high_colour_0,
34 bg_high_colour_1,
35 bg_high_colour_2,
36 bg_greyscale_colour_0,
37 bg_greyscale_colour_1,
38 bg_true_colour_0,
39 bg_true_colour_1,
40 bg_true_colour_2,
41 bg_true_colour_3,
42 bg_true_colour_4,
43 bg_true_colour_5,
44 utf8_0,
45 utf8_1,
46 utf8_2,
47 utf8_3,
48 done,
49};
50
51struct parser_info
52{
53 parser_state state{parser_state::idle};
54 byte charcode{0};
55 byte red{0};
56 byte green{0};
57 byte blue{0};
58 byte greyscale{0};
59 uint16_t utf8{0};
60};
61
62constexpr byte digit10_to_byte(char const ch)
63{
64 return static_cast<byte>(ch - '0');
65}
66
67constexpr byte digit16_to_byte(char const ch)
68{
69 return (ch >= '0' && ch <= '9') ? static_cast<byte>(ch - '0')
70 : (ch >= 'a' && ch <= 'f') ? static_cast<byte>((ch - 'a') + 10)
71 : (ch >= 'A' && ch <= 'F') ? static_cast<byte>((ch - 'A') + 10)
72 : static_cast<byte>(0);
73}
74
75constexpr void parse_utf8_3(char const ch, parser_info &info, element &elem)
76{
77 constexpr std::uint64_t const maxima[] = {
78 0x00007F, 0x0007FF, 0x00FFFF, 0x10FFFF};
79
80 byte text[4] = {0};
81 uint16_t const value = (info.utf8 * 16) + digit16_to_byte(ch);
82
83 // At the moment, we can only convert up to 0xFFFF hex, since we only have
84 // three spots in ucharacter_ to play with. As an arbitrary decision,
85 // anything above that will come out as a ? character. Otherwise,
86 // we will UTF-8 encode the value as appropriate into the ucharacter_
87 // array.
88 if (value <= maxima[0])
89 {
90 text[0] = byte(value & 0x7F);
91 text[1] = 0;
92 text[2] = 0;
93 }
94 else if (value <= maxima[1])
95 {
96 text[0] = byte(0b11000000 | (value >> 6));
97 text[1] = byte(0b10000000 | (value & 0b00111111));
98 text[2] = 0;
99 }
100 else if (value <= maxima[2])
101 {
102 text[0] = byte(0b11100000 | (value >> 12));
103 text[1] = byte(0b10000000 | ((value >> 6) & 0b00111111));
104 text[2] = byte(0b10000000 | (value & 0b00111111));
105 }
106 else
107 {
108 // Too high to encode right now.
109 text[0] = byte('?');
110 text[1] = 0;
111 }
112
113 elem.glyph_ = terminalpp::glyph(text);
114 info.state = parser_state::done;
115}
116
117constexpr void parse_utf8_2(char const ch, parser_info &info, element &elem)
118{
119 info.utf8 *= 16;
120 info.utf8 += digit16_to_byte(ch);
121 info.state = parser_state::utf8_3;
122}
123
124constexpr void parse_utf8_1(char const ch, parser_info &info, element &elem)
125{
126 info.utf8 *= 16;
127 info.utf8 += digit16_to_byte(ch);
128 info.state = parser_state::utf8_2;
129}
130
131constexpr void parse_utf8_0(char const ch, parser_info &info, element &elem)
132{
133 info.utf8 = digit16_to_byte(ch);
134 info.state = parser_state::utf8_1;
135}
136
137constexpr void parse_bg_true_colour_5(
138 char const ch, parser_info &info, element &elem)
139{
140 info.blue |= digit16_to_byte(ch);
141
142 elem.attribute_.background_colour_ =
143 true_colour{info.red, info.green, info.blue};
144
145 info.state = parser_state::idle;
146}
147
148constexpr void parse_bg_true_colour_4(
149 char const ch, parser_info &info, element &elem)
150{
151 info.blue = digit16_to_byte(ch) << 4;
152 info.state = parser_state::bg_true_colour_5;
153}
154
155constexpr void parse_bg_true_colour_3(
156 char const ch, parser_info &info, element &elem)
157{
158 info.green |= digit16_to_byte(ch);
159 info.state = parser_state::bg_true_colour_4;
160}
161
162constexpr void parse_bg_true_colour_2(
163 char const ch, parser_info &info, element &elem)
164{
165 info.green = digit16_to_byte(ch) << 4;
166 info.state = parser_state::bg_true_colour_3;
167}
168
169constexpr void parse_bg_true_colour_1(
170 char const ch, parser_info &info, element &elem)
171{
172 info.red |= digit16_to_byte(ch);
173 info.state = parser_state::bg_true_colour_2;
174}
175
176constexpr void parse_bg_true_colour_0(
177 char const ch, parser_info &info, element &elem)
178{
179 info.red = digit16_to_byte(ch) << 4;
180 info.state = parser_state::bg_true_colour_1;
181}
182
183constexpr void parse_bg_greyscale_1(
184 char const ch, parser_info &info, element &elem)
185{
186 byte const col = (info.greyscale * 10) + digit10_to_byte(ch);
187 elem.attribute_.background_colour_ = greyscale_colour(col);
188 info.state = parser_state::idle;
189}
190
191constexpr void parse_bg_greyscale_0(
192 char const ch, parser_info &info, element &elem)
193{
194 info.greyscale = digit10_to_byte(ch);
195 info.state = parser_state::bg_greyscale_colour_1;
196}
197
198constexpr void parse_bg_high_colour_2(
199 char const ch, parser_info &info, element &elem)
200{
201 auto const blue = digit10_to_byte(ch);
202 elem.attribute_.background_colour_ =
203 high_colour(info.red, info.green, blue);
204 info.state = parser_state::idle;
205}
206
207constexpr void parse_bg_high_colour_1(
208 char const ch, parser_info &info, element &elem)
209{
210 info.green = digit10_to_byte(ch);
211 info.state = parser_state::bg_high_colour_2;
212}
213
214constexpr void parse_bg_high_colour_0(
215 char const ch, parser_info &info, element &elem)
216{
217 info.red = digit10_to_byte(ch);
218 info.state = parser_state::bg_high_colour_1;
219}
220
221constexpr void parse_bg_low_colour(
222 char const ch, parser_info &info, element &elem)
223{
224 auto const col_code = digit10_to_byte(ch);
225 auto const col = static_cast<terminalpp::graphics::colour>(col_code);
226
227 elem.attribute_.background_colour_ = low_colour(col);
228 info.state = parser_state::idle;
229}
230
231constexpr void parse_fg_greyscale_1(
232 char const ch, parser_info &info, element &elem)
233{
234 byte const col = (info.greyscale * 10) + digit10_to_byte(ch);
235 elem.attribute_.foreground_colour_ = greyscale_colour(col);
236 info.state = parser_state::idle;
237}
238
239constexpr void parse_fg_greyscale_0(
240 char const ch, parser_info &info, element &elem)
241{
242 info.greyscale = digit10_to_byte(ch);
243 info.state = parser_state::fg_greyscale_colour_1;
244}
245
246constexpr void parse_fg_true_colour_5(
247 char const ch, parser_info &info, element &elem)
248{
249 info.blue |= digit16_to_byte(ch);
250
251 elem.attribute_.foreground_colour_ =
252 true_colour{info.red, info.green, info.blue};
253
254 info.state = parser_state::idle;
255}
256
257constexpr void parse_fg_true_colour_4(
258 char const ch, parser_info &info, element &elem)
259{
260 info.blue = digit16_to_byte(ch) << 4;
261 info.state = parser_state::fg_true_colour_5;
262}
263
264constexpr void parse_fg_true_colour_3(
265 char const ch, parser_info &info, element &elem)
266{
267 info.green |= digit16_to_byte(ch);
268 info.state = parser_state::fg_true_colour_4;
269}
270
271constexpr void parse_fg_true_colour_2(
272 char const ch, parser_info &info, element &elem)
273{
274 info.green = digit16_to_byte(ch) << 4;
275 info.state = parser_state::fg_true_colour_3;
276}
277
278constexpr void parse_fg_true_colour_1(
279 char const ch, parser_info &info, element &elem)
280{
281 info.red |= digit16_to_byte(ch);
282 info.state = parser_state::fg_true_colour_2;
283}
284
285constexpr void parse_fg_true_colour_0(
286 char const ch, parser_info &info, element &elem)
287{
288 info.red = digit16_to_byte(ch) << 4;
289 info.state = parser_state::fg_true_colour_1;
290}
291
292constexpr void parse_fg_high_colour_2(
293 char const ch, parser_info &info, element &elem)
294{
295 auto const blue = digit10_to_byte(ch);
296 elem.attribute_.foreground_colour_ =
297 high_colour(info.red, info.green, blue);
298 info.state = parser_state::idle;
299}
300
301constexpr void parse_fg_high_colour_1(
302 char const ch, parser_info &info, element &elem)
303{
304 info.green = digit10_to_byte(ch);
305 info.state = parser_state::fg_high_colour_2;
306}
307
308constexpr void parse_fg_high_colour_0(
309 char const ch, parser_info &info, element &elem)
310{
311 info.red = digit10_to_byte(ch);
312 info.state = parser_state::fg_high_colour_1;
313}
314
315constexpr void parse_fg_low_colour(
316 char const ch, parser_info &info, element &elem)
317{
318 auto const col_code = digit10_to_byte(ch);
319 auto const col = static_cast<terminalpp::graphics::colour>(col_code);
320
321 elem.attribute_.foreground_colour_ = low_colour(col);
322 info.state = parser_state::idle;
323}
324
325constexpr void parse_underlining(
326 char const ch, parser_info &info, element &elem)
327{
328 switch (ch)
329 {
330 case '+':
331 elem.attribute_.underlining_ = graphics::underlining::underlined;
332 break;
333
334 case '-':
335 elem.attribute_.underlining_ =
336 graphics::underlining::not_underlined;
337 break;
338
339 default:
340 elem.attribute_.underlining_ =
341 graphics::underlining::not_underlined;
342 break;
343 }
344
345 info.state = parser_state::idle;
346}
347
348constexpr void parse_polarity(char const ch, parser_info &info, element &elem)
349{
350 switch (ch)
351 {
352 case '+':
353 elem.attribute_.polarity_ = graphics::polarity::positive;
354 break;
355
356 case '-':
357 elem.attribute_.polarity_ = graphics::polarity::negative;
358 break;
359
360 default:
361 elem.attribute_.polarity_ = graphics::polarity::positive;
362 break;
363 }
364
365 info.state = parser_state::idle;
366}
367
368constexpr void parse_intensity(char const ch, parser_info &info, element &elem)
369{
370 switch (ch)
371 {
372 case '>':
373 elem.attribute_.intensity_ = graphics::intensity::bold;
374 break;
375
376 case '<':
377 elem.attribute_.intensity_ = graphics::intensity::faint;
378 break;
379
380 default:
381 elem.attribute_.intensity_ = graphics::intensity::normal;
382 break;
383 }
384
385 info.state = parser_state::idle;
386}
387
388constexpr void parse_charset_ext(
389 char const ch, parser_info &info, element &elem)
390{
391 byte const charset_code[] = {ansi::charset_extender, static_cast<byte>(ch)};
392 auto const charset = lookup_character_set(charset_code);
393 elem.glyph_.charset_ = charset ? *charset : elem.glyph_.charset_;
394 info.state = parser_state::idle;
395}
396
397constexpr void parse_charset(char const ch, parser_info &info, element &elem)
398{
399 switch (ch)
400 {
401 case '%':
402 info.state = parser_state::charset_ext;
403 break;
404
405 default:
406 {
407 byte const charset_code[] = {static_cast<byte>(ch)};
408 auto const charset = lookup_character_set(charset_code);
409 elem.glyph_.charset_ = charset ? *charset : elem.glyph_.charset_;
410 info.state = parser_state::idle;
411 }
412 }
413}
414
415constexpr void parse_charcode_2(char const ch, parser_info &info, element &elem)
416{
417 info.charcode *= 10;
418 info.charcode += digit10_to_byte(ch);
419 elem.glyph_.character_ = info.charcode;
420 info.state = parser_state::done;
421}
422
423constexpr void parse_charcode_1(char const ch, parser_info &info, element &elem)
424{
425 info.charcode *= 10;
426 info.charcode += digit10_to_byte(ch);
427 info.state = parser_state::charcode_2;
428}
429
430constexpr void parse_charcode_0(char const ch, parser_info &info, element &elem)
431{
432 info.charcode = digit10_to_byte(ch);
433 info.state = parser_state::charcode_1;
434}
435
436constexpr void parse_escape(char const ch, parser_info &info, element &elem)
437{
438 switch (ch)
439 {
440 case 'C':
441 info.state = parser_state::charcode_0;
442 break;
443
444 case 'c':
445 info.state = parser_state::charset;
446 break;
447
448 case 'i':
449 info.state = parser_state::intensity;
450 break;
451
452 case 'p':
453 info.state = parser_state::polarity;
454 break;
455
456 case 'u':
457 info.state = parser_state::underlining;
458 break;
459
460 case '[':
461 info.state = parser_state::fg_low_colour;
462 break;
463
464 case '<':
465 info.state = parser_state::fg_high_colour_0;
466 break;
467
468 case '{':
469 info.state = parser_state::fg_greyscale_colour_0;
470 break;
471
472 case '(':
473 info.state = parser_state::fg_true_colour_0;
474 break;
475
476 case ']':
477 info.state = parser_state::bg_low_colour;
478 break;
479
480 case '>':
481 info.state = parser_state::bg_high_colour_0;
482 break;
483
484 case '}':
485 info.state = parser_state::bg_greyscale_colour_0;
486 break;
487
488 case ')':
489 info.state = parser_state::bg_true_colour_0;
490 break;
491
492 case 'U':
493 info.state = parser_state::utf8_0;
494 break;
495
496 case 'x':
497 elem.attribute_ = {};
498 info.state = parser_state::idle;
499 break;
500
501 default:
502 elem.glyph_.character_ = static_cast<byte>(ch);
503 info.state = parser_state::done;
504 break;
505 }
506}
507
508constexpr void parse_idle(char const ch, parser_info &info, element &elem)
509{
510 switch (ch)
511 {
512 case '\\':
513 info.state = parser_state::escape;
514 break;
515
516 default:
517 elem.glyph_.character_ = static_cast<byte>(ch);
518 info.state = parser_state::done;
519 break;
520 }
521}
522
523constexpr element element_with_base(element const &elem_base)
524{
525 element result = elem_base;
526 result.glyph_.charset_ = result.glyph_.charset_ == charset::utf8
527 ? charset::us_ascii
528 : result.glyph_.charset_;
529 result.glyph_.character_ = ' ';
530
531 return result;
532}
533
534constexpr element parse_element(
535 std::span<char const> &text, element const &elem_base)
536{
537 auto info = parser_info{};
538 auto elem = element_with_base(elem_base);
539
540 using handler = void (*)(char, parser_info &, element &);
541 handler const handlers[] = {
542 parse_idle,
543 parse_escape,
544 parse_charcode_0,
545 parse_charcode_1,
546 parse_charcode_2,
547 parse_charset,
548 parse_charset_ext,
549 parse_intensity,
550 parse_polarity,
551 parse_underlining,
552 parse_fg_low_colour,
553 parse_fg_high_colour_0,
554 parse_fg_high_colour_1,
555 parse_fg_high_colour_2,
556 parse_fg_greyscale_0,
557 parse_fg_greyscale_1,
558 parse_fg_true_colour_0,
559 parse_fg_true_colour_1,
560 parse_fg_true_colour_2,
561 parse_fg_true_colour_3,
562 parse_fg_true_colour_4,
563 parse_fg_true_colour_5,
564 parse_bg_low_colour,
565 parse_bg_high_colour_0,
566 parse_bg_high_colour_1,
567 parse_bg_high_colour_2,
568 parse_bg_greyscale_0,
569 parse_bg_greyscale_1,
570 parse_bg_true_colour_0,
571 parse_bg_true_colour_1,
572 parse_bg_true_colour_2,
573 parse_bg_true_colour_3,
574 parse_bg_true_colour_4,
575 parse_bg_true_colour_5,
576 parse_utf8_0,
577 parse_utf8_1,
578 parse_utf8_2,
579 parse_utf8_3,
580 };
581
582 while (!text.empty() && info.state != parser_state::done)
583 {
584 handlers[static_cast<int>(info.state)](text[0], info, elem);
585 text = text.subspan(1);
586 }
587
588 return elem;
589}
590
591} // namespace terminalpp::detail
A structure that carries around the character attributes of an ANSI element.
Definition glyph.hpp:19