#include #include #include "dbc.hpp" #include #include "ansi_parser.hpp" #include using namespace fmt; %%{ machine ansi_parser; alphtype int; action tstart { start = fpc; } action number { value = 0; size_t len = fpc - start; dbc::check(start[0] != '-', "negative numbers not supported"); switch(len) { case 10: value += (start[len-10] - '0') * 1000000000; case 9: value += (start[len- 9] - '0') * 100000000; case 8: value += (start[len- 8] - '0') * 10000000; case 7: value += (start[len- 7] - '0') * 1000000; case 6: value += (start[len- 6] - '0') * 100000; case 5: value += (start[len- 5] - '0') * 10000; case 4: value += (start[len- 4] - '0') * 1000; case 3: value += (start[len- 3] - '0') * 100; case 2: value += (start[len- 2] - '0') * 10; case 1: value += (start[len- 1] - '0'); break; default: dbc::sentinel("can't process > 10 digits"); } } action color_out { color_cb(color, bgcolor); } action is_fg { target = &color; } action is_bg { target = &bgcolor; } action out { write_cb(fc); } action reset_fg { color = $default_fg; color_cb(color, bgcolor); } action reset_bg { bgcolor = $default_bg; color_cb(color, bgcolor); } action invert { color = $default_bg; bgcolor = $default_fg; color_cb(color, bgcolor); } action reset_invert { color = $default_fg; bgcolor = $default_bg; color_cb(color, bgcolor); } action half_bright { color = sf::Color(100,100,100); color_cb(color, bgcolor); } action red_text { color = sf::Color::Red; color_cb(color, bgcolor); } action red { target->r = value; } action blue { target->g = value; } action green { target->b = value; } action start { value = 0; } action end {} action log { println("command {}", (char)fc); } ESC = 0x1B; start = ESC "["; fg = "38;" %is_fg; bg = "48;" %is_bg; reset = ("39" %reset_fg | "49" %reset_bg); num = digit+ >tstart %number; color256 = "5;"; color24b = "2;"; ansi = ( start %start ( reset | "0" | "1" | "2" %half_bright | "3" | "4" | "5" | "6" | "7" %invert | "31" %red_text | "22" | "27" %reset_invert | "9" ["0"-"7"] | "10" ["0"-"7"] | (fg|bg) (color24b num %red ";" num %blue ";" num %green ) %color_out ) "m" %end ); other = (any+ @out -- ESC)*; main := (other :> ansi)**; }%% %% write data; #include ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : $default_fg(default_fg), $default_bg(default_bg) { // the parser only handles full color so force it if(ftxui::Terminal::ColorSupport() != ftxui::Terminal::Color::TrueColor) { ftxui::Terminal::SetColorSupport(ftxui::Terminal::Color::TrueColor); } } bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { const wchar_t *start = NULL; int cs = 0; unsigned int value = 0; const wchar_t *p = codes.data(); const wchar_t *pe = p + codes.size(); const wchar_t *eof = pe; sf::Color bgcolor($default_bg); sf::Color color($default_fg); sf::Color* target = &color; %% write init; %% write exec; bool good = pe - p == 0; if(!good) { p -= 10; // dear cthuhlu, save me from the pain that is wstring for(int i = 0; i < 100; i++) { try { print("{}", p[i] == 0x1B ? '^' : char(p[i])); } catch(...) { print("?=", int(p[i])); } } } return good; }