Found my old LEL code and got the shell working, so tomorrow I'll try to make it layout some gui element.
This commit is contained in:
		
							parent
							
								
									8a6b38c1a4
								
							
						
					
					
						commit
						bfd2718cc9
					
				
					 8 changed files with 485 additions and 3 deletions
				
			
		
							
								
								
									
										2
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -6,7 +6,7 @@ reset: | |||
| %.cpp : %.rl | ||||
| 	ragel -o $@ $< | ||||
| 
 | ||||
| build: ansi_parser.cpp | ||||
| build: ansi_parser.cpp lel.cpp | ||||
| 	meson compile -j 10 -C builddir | ||||
| 
 | ||||
| release_build: | ||||
|  |  | |||
|  | @ -114,7 +114,7 @@ ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : | |||
| } | ||||
| 
 | ||||
| bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { | ||||
|   const wchar_t *start = NULL; | ||||
|   const wchar_t *start = nullptr; | ||||
|   int cs = 0; | ||||
|   unsigned int value = 0; | ||||
|   const wchar_t *p = codes.data(); | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : | |||
| } | ||||
| 
 | ||||
| bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { | ||||
|   const wchar_t *start = NULL; | ||||
|   const wchar_t *start = nullptr; | ||||
|   int cs = 0; | ||||
|   unsigned int value = 0; | ||||
|   const wchar_t *p = codes.data(); | ||||
|  |  | |||
							
								
								
									
										294
									
								
								lel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								lel.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,294 @@ | |||
| 
 | ||||
| #line 1 "lel.rl" | ||||
| #include "lel.hpp" | ||||
| #include <fmt/core.h> | ||||
| 
 | ||||
| 
 | ||||
| #line 33 "lel.rl" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #line 7 "lel.cpp" | ||||
| static const char _LELParser_actions[] = { | ||||
| 	0, 1, 1, 1, 2, 1, 3, 1,  | ||||
| 	4, 1, 5, 1, 6, 1, 9, 1,  | ||||
| 	10, 1, 11, 2, 0, 7, 2, 0,  | ||||
| 	8, 2, 4, 1, 2, 4, 5 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_key_offsets[] = { | ||||
| 	0, 0, 4, 18, 20, 24, 35, 47,  | ||||
| 	52, 66, 68, 72, 83, 95, 99, 101,  | ||||
| 	104, 106, 109 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_trans_keys[] = { | ||||
| 	32, 91, 9, 13, 32, 40, 42, 46,  | ||||
| 	60, 62, 94, 95, 9, 13, 65, 90,  | ||||
| 	97, 122, 48, 57, 41, 44, 48, 57,  | ||||
| 	40, 42, 46, 60, 62, 94, 95, 65,  | ||||
| 	90, 97, 122, 32, 93, 95, 124, 9,  | ||||
| 	13, 48, 57, 65, 90, 97, 122, 32,  | ||||
| 	93, 124, 9, 13, 32, 40, 42, 46,  | ||||
| 	60, 62, 94, 95, 9, 13, 65, 90,  | ||||
| 	97, 122, 48, 57, 41, 44, 48, 57,  | ||||
| 	40, 42, 46, 60, 62, 94, 95, 65,  | ||||
| 	90, 97, 122, 32, 93, 95, 124, 9,  | ||||
| 	13, 48, 57, 65, 90, 97, 122, 32,  | ||||
| 	93, 9, 13, 48, 57, 41, 48, 57,  | ||||
| 	48, 57, 41, 48, 57, 32, 91, 9,  | ||||
| 	13, 0 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_single_lengths[] = { | ||||
| 	0, 2, 8, 0, 2, 7, 4, 3,  | ||||
| 	8, 0, 2, 7, 4, 2, 0, 1,  | ||||
| 	0, 1, 2 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_range_lengths[] = { | ||||
| 	0, 1, 3, 1, 1, 2, 4, 1,  | ||||
| 	3, 1, 1, 2, 4, 1, 1, 1,  | ||||
| 	1, 1, 1 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_index_offsets[] = { | ||||
| 	0, 0, 4, 16, 18, 22, 32, 41,  | ||||
| 	46, 58, 60, 64, 74, 83, 87, 89,  | ||||
| 	92, 94, 97 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_indicies[] = { | ||||
| 	0, 2, 0, 1, 3, 4, 5, 6,  | ||||
| 	7, 7, 6, 8, 3, 8, 8, 1,  | ||||
| 	9, 1, 10, 11, 12, 1, 4, 5,  | ||||
| 	6, 7, 7, 6, 8, 8, 8, 1,  | ||||
| 	13, 15, 14, 16, 13, 14, 14, 14,  | ||||
| 	1, 17, 18, 19, 17, 1, 20, 21,  | ||||
| 	22, 23, 24, 24, 23, 25, 20, 25,  | ||||
| 	25, 1, 26, 1, 27, 28, 29, 1,  | ||||
| 	21, 22, 23, 24, 24, 23, 25, 25,  | ||||
| 	25, 1, 30, 15, 31, 16, 30, 31,  | ||||
| 	31, 31, 1, 32, 18, 32, 1, 33,  | ||||
| 	1, 34, 35, 1, 36, 1, 37, 38,  | ||||
| 	1, 39, 2, 39, 1, 0 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_trans_targs[] = { | ||||
| 	1, 0, 2, 2, 3, 5, 5, 5,  | ||||
| 	6, 4, 5, 16, 4, 7, 6, 18,  | ||||
| 	8, 7, 18, 8, 8, 9, 11, 11,  | ||||
| 	11, 12, 10, 11, 14, 10, 13, 12,  | ||||
| 	13, 15, 11, 15, 17, 5, 17, 18 | ||||
| }; | ||||
| 
 | ||||
| static const char _LELParser_trans_actions[] = { | ||||
| 	0, 0, 3, 0, 0, 13, 5, 11,  | ||||
| 	17, 15, 19, 19, 0, 7, 0, 28,  | ||||
| 	25, 0, 9, 1, 0, 0, 13, 5,  | ||||
| 	11, 17, 15, 19, 19, 0, 7, 0,  | ||||
| 	0, 15, 22, 0, 15, 22, 0, 0 | ||||
| }; | ||||
| 
 | ||||
| static const int LELParser_start = 1; | ||||
| static const int LELParser_first_final = 18; | ||||
| static const int LELParser_error = 0; | ||||
| 
 | ||||
| static const int LELParser_en_main = 1; | ||||
| 
 | ||||
| 
 | ||||
| #line 36 "lel.rl" | ||||
| 
 | ||||
| bool LELParser::parse(std::string input) { | ||||
|   int cs = 0; | ||||
|   const char *start = nullptr; | ||||
|   const char *begin = input.data(); | ||||
|   const char *p = input.data(); | ||||
|   const char *pe = p + input.size(); | ||||
|   std::string tk; | ||||
| 
 | ||||
|    | ||||
| #line 103 "lel.cpp" | ||||
| 	{ | ||||
| 	cs = LELParser_start; | ||||
| 	} | ||||
| 
 | ||||
| #line 46 "lel.rl" | ||||
|    | ||||
| #line 106 "lel.cpp" | ||||
| 	{ | ||||
| 	int _klen; | ||||
| 	unsigned int _trans; | ||||
| 	const char *_acts; | ||||
| 	unsigned int _nacts; | ||||
| 	const char *_keys; | ||||
| 
 | ||||
| 	if ( p == pe ) | ||||
| 		goto _test_eof; | ||||
| 	if ( cs == 0 ) | ||||
| 		goto _out; | ||||
| _resume: | ||||
| 	_keys = _LELParser_trans_keys + _LELParser_key_offsets[cs]; | ||||
| 	_trans = _LELParser_index_offsets[cs]; | ||||
| 
 | ||||
| 	_klen = _LELParser_single_lengths[cs]; | ||||
| 	if ( _klen > 0 ) { | ||||
| 		const char *_lower = _keys; | ||||
| 		const char *_mid; | ||||
| 		const char *_upper = _keys + _klen - 1; | ||||
| 		while (1) { | ||||
| 			if ( _upper < _lower ) | ||||
| 				break; | ||||
| 
 | ||||
| 			_mid = _lower + ((_upper-_lower) >> 1); | ||||
| 			if ( (*p) < *_mid ) | ||||
| 				_upper = _mid - 1; | ||||
| 			else if ( (*p) > *_mid ) | ||||
| 				_lower = _mid + 1; | ||||
| 			else { | ||||
| 				_trans += (unsigned int)(_mid - _keys); | ||||
| 				goto _match; | ||||
| 			} | ||||
| 		} | ||||
| 		_keys += _klen; | ||||
| 		_trans += _klen; | ||||
| 	} | ||||
| 
 | ||||
| 	_klen = _LELParser_range_lengths[cs]; | ||||
| 	if ( _klen > 0 ) { | ||||
| 		const char *_lower = _keys; | ||||
| 		const char *_mid; | ||||
| 		const char *_upper = _keys + (_klen<<1) - 2; | ||||
| 		while (1) { | ||||
| 			if ( _upper < _lower ) | ||||
| 				break; | ||||
| 
 | ||||
| 			_mid = _lower + (((_upper-_lower) >> 1) & ~1); | ||||
| 			if ( (*p) < _mid[0] ) | ||||
| 				_upper = _mid - 2; | ||||
| 			else if ( (*p) > _mid[1] ) | ||||
| 				_lower = _mid + 2; | ||||
| 			else { | ||||
| 				_trans += (unsigned int)((_mid - _keys)>>1); | ||||
| 				goto _match; | ||||
| 			} | ||||
| 		} | ||||
| 		_trans += _klen; | ||||
| 	} | ||||
| 
 | ||||
| _match: | ||||
| 	_trans = _LELParser_indicies[_trans]; | ||||
| 	cs = _LELParser_trans_targs[_trans]; | ||||
| 
 | ||||
| 	if ( _LELParser_trans_actions[_trans] == 0 ) | ||||
| 		goto _again; | ||||
| 
 | ||||
| 	_acts = _LELParser_actions + _LELParser_trans_actions[_trans]; | ||||
| 	_nacts = (unsigned int) *_acts++; | ||||
| 	while ( _nacts-- > 0 ) | ||||
| 	{ | ||||
| 		switch ( *_acts++ ) | ||||
| 		{ | ||||
| 	case 0: | ||||
| #line 8 "lel.rl" | ||||
| 	{tk = input.substr(start - begin, p - start); } | ||||
| 	break; | ||||
| 	case 1: | ||||
| #line 10 "lel.rl" | ||||
| 	{ col(); } | ||||
| 	break; | ||||
| 	case 2: | ||||
| #line 11 "lel.rl" | ||||
| 	{ ltab(); } | ||||
| 	break; | ||||
| 	case 3: | ||||
| #line 12 "lel.rl" | ||||
| 	{ valign((*p)); } | ||||
| 	break; | ||||
| 	case 4: | ||||
| #line 13 "lel.rl" | ||||
| 	{ id(input.substr(start - begin, p - start)); } | ||||
| 	break; | ||||
| 	case 5: | ||||
| #line 14 "lel.rl" | ||||
| 	{ row(); } | ||||
| 	break; | ||||
| 	case 6: | ||||
| #line 15 "lel.rl" | ||||
| 	{ align((*p)); } | ||||
| 	break; | ||||
| 	case 7: | ||||
| #line 16 "lel.rl" | ||||
| 	{ setwidth(std::stoi(tk)); } | ||||
| 	break; | ||||
| 	case 8: | ||||
| #line 17 "lel.rl" | ||||
| 	{ setheight(std::stoi(tk)); } | ||||
| 	break; | ||||
| 	case 9: | ||||
| #line 18 "lel.rl" | ||||
| 	{ expand(); } | ||||
| 	break; | ||||
| 	case 10: | ||||
| #line 26 "lel.rl" | ||||
| 	{ start = p; } | ||||
| 	break; | ||||
| 	case 11: | ||||
| #line 29 "lel.rl" | ||||
| 	{start = p;} | ||||
| 	break; | ||||
| #line 215 "lel.cpp" | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| _again: | ||||
| 	if ( cs == 0 ) | ||||
| 		goto _out; | ||||
| 	if ( ++p != pe ) | ||||
| 		goto _resume; | ||||
| 	_test_eof: {} | ||||
| 	_out: {} | ||||
| 	} | ||||
| 
 | ||||
| #line 47 "lel.rl" | ||||
| 
 | ||||
|   bool good = pe - p == 0; | ||||
| 
 | ||||
|   return good; | ||||
| } | ||||
| 
 | ||||
| void LELParser::col() { | ||||
|   fmt::println("col"); | ||||
| } | ||||
| 
 | ||||
| void LELParser::ltab() { | ||||
|   fmt::println("ltab"); | ||||
| } | ||||
| 
 | ||||
| void LELParser::valign(char dir) { | ||||
|   fmt::println("valign: {}", dir); | ||||
| } | ||||
| 
 | ||||
| void LELParser::align(char dir) { | ||||
|   fmt::println("align {}", dir); | ||||
| } | ||||
| 
 | ||||
| void LELParser::id(std::string name) { | ||||
|   fmt::println("id: {}", name); | ||||
| } | ||||
| 
 | ||||
| void LELParser::row() { | ||||
|   fmt::println("row"); | ||||
| } | ||||
| 
 | ||||
| void LELParser::setwidth(int width) { | ||||
|   fmt::println("setwidth: {}", width); | ||||
| } | ||||
| 
 | ||||
| void LELParser::setheight(int height) { | ||||
|   fmt::println("setheight: {}", height); | ||||
| } | ||||
| 
 | ||||
| void LELParser::expand() { | ||||
|   fmt::println("expand"); | ||||
| } | ||||
							
								
								
									
										87
									
								
								lel.rl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								lel.rl
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| #include "lel.hpp" | ||||
| #include <fmt/core.h> | ||||
| 
 | ||||
| %%{ | ||||
|   machine LELParser; | ||||
|   alphtype char; | ||||
| 
 | ||||
|   action token {tk = input.substr(start - begin, fpc - start); } | ||||
| 
 | ||||
|   action col { col(); } | ||||
|   action ltab { ltab(); } | ||||
|   action valign { valign(fc); } | ||||
|   action id { id(input.substr(start - begin, fpc - start)); } | ||||
|   action row { row(); } | ||||
|   action align { align(fc); } | ||||
|   action setwidth { setwidth(std::stoi(tk)); } | ||||
|   action setheight { setheight(std::stoi(tk)); } | ||||
|   action expand { expand(); } | ||||
| 
 | ||||
|   col = "|" $col; | ||||
|   ltab = "[" $ltab; | ||||
|   rtab = "]" $row; | ||||
|   valign = ("^" | ".") $valign; | ||||
|   expand = "*" $expand; | ||||
|   halign = ("<" | ">") $align; | ||||
|   number =  digit+ >{ start = fpc; } %token; | ||||
|   setw = ("(" number %setwidth ("," number %setheight)? ")") ; | ||||
|   modifiers = (expand | valign | halign | setw); | ||||
|   id = modifiers* ((alpha | '_')+ :>> (alnum | '_')*) >{start = fpc;} %id; | ||||
|   row = space* ltab space* id space* (col space* id)* space* rtab space*; | ||||
| 
 | ||||
|   main := row+; | ||||
| }%% | ||||
| 
 | ||||
| %% write data; | ||||
| 
 | ||||
| bool LELParser::parse(std::string input) { | ||||
|   int cs = 0; | ||||
|   const char *start = nullptr; | ||||
|   const char *begin = input.data(); | ||||
|   const char *p = input.data(); | ||||
|   const char *pe = p + input.size(); | ||||
|   std::string tk; | ||||
| 
 | ||||
|   %% write init; | ||||
|   %% write exec; | ||||
| 
 | ||||
|   bool good = pe - p == 0; | ||||
| 
 | ||||
|   return good; | ||||
| } | ||||
| 
 | ||||
| void LELParser::col() { | ||||
|   fmt::println("col"); | ||||
| } | ||||
| 
 | ||||
| void LELParser::ltab() { | ||||
|   fmt::println("ltab"); | ||||
| } | ||||
| 
 | ||||
| void LELParser::valign(char dir) { | ||||
|   fmt::println("valign: {}", dir); | ||||
| } | ||||
| 
 | ||||
| void LELParser::align(char dir) { | ||||
|   fmt::println("align {}", dir); | ||||
| } | ||||
| 
 | ||||
| void LELParser::id(std::string name) { | ||||
|   fmt::println("id: {}", name); | ||||
| } | ||||
| 
 | ||||
| void LELParser::row() { | ||||
|   fmt::println("row"); | ||||
| } | ||||
| 
 | ||||
| void LELParser::setwidth(int width) { | ||||
|   fmt::println("setwidth: {}", width); | ||||
| } | ||||
| 
 | ||||
| void LELParser::setheight(int height) { | ||||
|   fmt::println("setheight: {}", height); | ||||
| } | ||||
| 
 | ||||
| void LELParser::expand() { | ||||
|   fmt::println("expand"); | ||||
| } | ||||
|  | @ -54,6 +54,7 @@ sources = [ | |||
|   'devices.cpp', | ||||
|   'gui.cpp', | ||||
|   'inventory.cpp', | ||||
|   'lel.cpp', | ||||
|   'levelmanager.cpp', | ||||
|   'lights.cpp', | ||||
|   'map.cpp', | ||||
|  | @ -77,11 +78,13 @@ sources = [ | |||
| ] | ||||
| 
 | ||||
| executable('runtests', sources + [ | ||||
|   'tests/ansi_parser.cpp', | ||||
|   'tests/base.cpp', | ||||
|   'tests/dbc.cpp', | ||||
|   'tests/dinkyecs.cpp', | ||||
|   'tests/fsm.cpp', | ||||
|   'tests/inventory.cpp', | ||||
|   'tests/lel.cpp', | ||||
|   'tests/levelmanager.cpp', | ||||
|   'tests/lighting.cpp', | ||||
|   'tests/map.cpp', | ||||
|  |  | |||
							
								
								
									
										77
									
								
								tests/ansi_parser.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								tests/ansi_parser.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include <fmt/core.h> | ||||
| #include <string> | ||||
| #include "ansi_parser.hpp" | ||||
| #include <codecvt> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "ftxui/dom/elements.hpp"  // for text, bgcolor, color, vbox, hbox, separator, operator|, Elements, Element, Fit, border
 | ||||
| #include "ftxui/dom/node.hpp"      // for Render
 | ||||
| #include "ftxui/screen/color.hpp"  // for Color, Color::Black, Color::Blue, Color::BlueLight, Color::Cyan, Color::CyanLight, Color::Default, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::RedLight, Color::White, Color::Yellow, Color::YellowLight, Color::Palette256, ftxui
 | ||||
| #include <ftxui/screen/color_info.hpp>  // for ColorInfo
 | ||||
| #include <ftxui/screen/screen.hpp>      // for Full, Screen
 | ||||
| #include <ftxui/screen/screen.hpp>      // for Full, Screen
 | ||||
| #include <ftxui/screen/terminal.hpp>  // for ColorSupport, Color, Palette16, Palette256, TrueColor
 | ||||
| #include "render.hpp" | ||||
| 
 | ||||
| using namespace fmt; | ||||
| using namespace ftxui; | ||||
| 
 | ||||
| std::string generate_colors() { | ||||
|   SFMLRender::init_terminal(); | ||||
| 
 | ||||
|   REQUIRE(ftxui::Terminal::ColorSupport() == ftxui::Terminal::Color::TrueColor); | ||||
| 
 | ||||
|   const int max_value = 255; | ||||
|   const int value_increment = 8; | ||||
|   const int hue_increment = 6; | ||||
|   int saturation = max_value; | ||||
|   Elements array; | ||||
|   int count = 0; | ||||
| 
 | ||||
|   for (int value = 0; value < max_value; value += 2 * value_increment) { | ||||
|     Elements line; | ||||
|     for (int hue = 0; hue < max_value; hue += hue_increment) { | ||||
|       line.push_back( | ||||
|           text("#")                                    //
 | ||||
|           | ftxui::color(Color::HSV(hue, saturation, value))  //
 | ||||
|           | ftxui::bgcolor(Color::HSV(hue, saturation, value + value_increment))); | ||||
|       count++; | ||||
|     } | ||||
|     array.push_back(hbox(std::move(line))); | ||||
|   } | ||||
| 
 | ||||
|   auto true_color_display = vbox({ | ||||
|       vbox(std::move(array)), | ||||
|   }); | ||||
| 
 | ||||
|   auto document = vbox({hbox({ | ||||
|       true_color_display, | ||||
|   })}); | ||||
| 
 | ||||
|   auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); | ||||
|   Render(screen, document); | ||||
|   return screen.ToString(); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("test out ragel parser", "[gui]") { | ||||
|   SFMLRender::init_terminal(); | ||||
|   sf::Color default_fg(0,0,0); | ||||
|   sf::Color default_bg(100,100,100); | ||||
| 
 | ||||
|   // this sets the Truecolor so need to do it first
 | ||||
|   ANSIParser ansi(default_fg, default_bg); | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter; | ||||
|   std::string colors = generate_colors(); | ||||
|   std::wstring colors_utf = $converter.from_bytes(colors); | ||||
| 
 | ||||
|   bool good = ansi.parse(colors_utf, | ||||
|       [&](sf::Color color[[maybe_unused]], sf::Color bgcolor[[maybe_unused]]){ | ||||
|       }, | ||||
|       [&](wchar_t ch) { | ||||
|       bool correct_char = ch == '#' || ch == ' ' || ch == '\n' || ch == '\r'; | ||||
|       REQUIRE(correct_char); | ||||
|   }); | ||||
| 
 | ||||
|   REQUIRE(good); | ||||
| } | ||||
							
								
								
									
										21
									
								
								tests/lel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/lel.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| #include "lel.hpp" | ||||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include <fmt/core.h> | ||||
| #include <string> | ||||
| #include "ansi_parser.hpp" | ||||
| #include <codecvt> | ||||
| #include <iostream> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| TEST_CASE("test basic ops", "[lel]") { | ||||
|   LELParser parser; | ||||
| 
 | ||||
|   bool good = parser.parse( | ||||
|      "[ label_1         | label3      ]" | ||||
|      "[ (300,300)*text1 | (150)people ]" | ||||
|      "[ <label2         | _           ]" | ||||
|      "[ message         | buttons     ]"); | ||||
| 
 | ||||
|   REQUIRE(good); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Zed A. Shaw
						Zed A. Shaw