char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
c96f608d9
char/aliceai-t5-moe
1#include "tests.h" 2 3#include "peg-parser.h" 4 5#include <string> 6#include <sstream> 7#include <iomanip> 8#include <cctype> 9 10static void assert_result_equal (testing & t ,common_peg_parse_result_type expected ,common_peg_parse_result_type actual ) { 11t .assert_equal (common_peg_parse_result_type_name (expected ),common_peg_parse_result_type_name (actual )); 12} 13 14static std::string hex_dump (const std::string & str ) { 15 std::ostringstream oss ; 16for (unsigned char c :str ) { 17if (std::isprint (c )) { 18oss <<c ; 19 }else { 20oss <<"\\x" << std::hex << std::setw (2 ) << std::setfill ('0' ) <<static_cast < int > (c ); 21 } 22 } 23return oss .str (); 24} 25 26void test_unicode (testing & t ) { 27struct test_case { 28 std::string input ; 29 std::string expected_text ; 30common_peg_parse_result_type expected_result ; 31 }; 32 33t .test ("any" , [](testing & t ) { 34 std::vector < test_case > test_cases { 35// Valid UTF-8 sequences 36 {"Hello" ,"Hello" ,COMMON_PEG_PARSE_RESULT_SUCCESS }, 37 {std::string ("Caf\xC3\xA9" ), std::string ("Caf\xC3\xA9" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 38 {std::string ("\xE4\xBD\xA0\xE5\xA5\xBD" ), std::string ("\xE4\xBD\xA0\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 39 {std::string ("\xF0\x9F\x9A\x80" ), std::string ("\xF0\x9F\x9A\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 40 41// Incomplete UTF-8 sequences (partial bytes at end) 42 {std::string ("Caf\xC3" ),"Caf" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 43 {std::string ("\xE4\xBD" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 44 {std::string ("\xF0\x9F\x9A" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 45 46// Invalid/malformed UTF-8 sequences 47 {std::string ("\xFF\xFE" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 48 {std::string ("Hello\x80World" ),"Hello" ,COMMON_PEG_PARSE_RESULT_FAIL }, 49 {std::string ("\xC3\x28" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 50 }; 51 52auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 53return p .sequence ({p .one_or_more (p .any ()),p .end ()}); 54 }); 55 56for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 57const auto & tc = test_cases [i ]; 58 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 59 60t .test (test_name , [& ](testing & t ) { 61common_peg_parse_context ctx (tc .input ,COMMON_PEG_PARSE_FLAG_LENIENT ); 62auto result = parser .parse (ctx ); 63 64// Assert result type matches 65assert_result_equal (t ,tc .expected_result ,result .type ); 66 67// Assert matched text if success or need_more_input 68if (result .success ()|| result .need_more_input ()) { 69 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 70t .assert_equal (tc .expected_text ,matched ); 71 } 72 }); 73 } 74 }); 75 76t .test ("char classes" , [](testing & t ) { 77t .test ("unicode range U+4E00-U+9FFF (CJK)" , [](testing & t ) { 78 std::vector < test_case > test_cases { 79// Within range - CJK Unified Ideographs 80 {std::string ("\xE4\xB8\x80" ), std::string ("\xE4\xB8\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+4E00 81 {std::string ("\xE4\xBD\xA0" ), std::string ("\xE4\xBD\xA0" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+4F60 82 {std::string ("\xE5\xA5\xBD" ), std::string ("\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+597D 83 {std::string ("\xE9\xBF\xBF" ), std::string ("\xE9\xBF\xBF" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+9FFF 84 85// Outside range - should fail 86 {"a" ,"" ,COMMON_PEG_PARSE_RESULT_FAIL },// ASCII 87 {std::string ("\xE4\xB7\xBF" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL },// U+4DFF (before range) 88 {std::string ("\xEA\x80\x80" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL },// U+A000 (after range) 89 90// Incomplete sequences in range 91 {std::string ("\xE4\xB8" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT },// Incomplete U+4E00 92 {std::string ("\xE5\xA5" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT },// Incomplete U+597D 93 }; 94 95auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 96return p .sequence ({p .chars (R"([\u4E00-\u9FFF])" ),p .end ()}); 97 }); 98 99for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 100const auto & tc = test_cases [i ]; 101 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 102 103t .test (test_name , [& ](testing & t ) { 104common_peg_parse_context ctx (tc .input ,COMMON_PEG_PARSE_FLAG_LENIENT ); 105auto result = parser .parse (ctx ); 106 107// Assert result type matches 108assert_result_equal (t ,tc .expected_result ,result .type ); 109 110// Assert matched text if success or need_more_input 111if (result .success ()|| result .need_more_input ()) { 112 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 113t .assert_equal (tc .expected_text ,matched ); 114 } 115 }); 116 } 117 }); 118 119t .test ("unicode range U+1F600-U+1F64F (emoticons)" , [](testing & t ) { 120 std::vector < test_case > test_cases { 121// Within range - Emoticons (all 4-byte UTF-8) 122 {std::string ("\xF0\x9F\x98\x80" ), std::string ("\xF0\x9F\x98\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+1F600 123 {std::string ("\xF0\x9F\x98\x81" ), std::string ("\xF0\x9F\x98\x81" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+1F601 124 {std::string ("\xF0\x9F\x99\x8F" ), std::string ("\xF0\x9F\x99\x8F" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+1F64F 125 126// Outside range 127 {std::string ("\xF0\x9F\x97\xBF" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL },// U+1F5FF (before range) 128 {std::string ("\xF0\x9F\x99\x90" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL },// U+1F650 (after range) 129 {std::string ("\xF0\x9F\x9A\x80" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL },// U+1F680 (outside range) 130 131// Incomplete sequences 132 {std::string ("\xF0\x9F\x98" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT },// Incomplete emoji 133 {std::string ("\xF0\x9F" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT },// Very incomplete 134 }; 135 136auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 137return p .sequence ({p .chars (R"([\U0001F600-\U0001F64F])" ),p .end ()}); 138 }); 139 140for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 141const auto & tc = test_cases [i ]; 142 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 143 144t .test (test_name , [& ](testing & t ) { 145common_peg_parse_context ctx (tc .input ,COMMON_PEG_PARSE_FLAG_LENIENT ); 146auto result = parser .parse (ctx ); 147 148// Assert result type matches 149assert_result_equal (t ,tc .expected_result ,result .type ); 150 151// Assert matched text if success or need_more_input 152if (result .success ()|| result .need_more_input ()) { 153 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 154t .assert_equal (tc .expected_text ,matched ); 155 } 156 }); 157 } 158 }); 159 160t .test ("mixed unicode ranges" , [](testing & t ) { 161 std::vector < test_case > test_cases { 162// Match CJK 163 {std::string ("\xE4\xB8\x80" ), std::string ("\xE4\xB8\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+4E00 164 {std::string ("\xE4\xBD\xA0" ), std::string ("\xE4\xBD\xA0" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+4F60 165 166// Match emoticons 167 {std::string ("\xF0\x9F\x98\x80" ), std::string ("\xF0\x9F\x98\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS },// U+1F600 168 169// Match ASCII digits 170 {"5" ,"5" ,COMMON_PEG_PARSE_RESULT_SUCCESS }, 171 172// Don't match outside any range 173 {"a" ,"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 174 {std::string ("\xF0\x9F\x9A\x80" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL },// U+1F680 175 176// Incomplete 177 {std::string ("\xE4\xB8" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 178 {std::string ("\xF0\x9F\x98" ),"" ,COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 179 }; 180 181auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 182return p .sequence ({p .chars (R"([\u4E00-\u9FFF\U0001F600-\U0001F64F0-9])" ),p .end ()}); 183 }); 184 185for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 186const auto & tc = test_cases [i ]; 187 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 188 189t .test (test_name , [& ](testing & t ) { 190common_peg_parse_context ctx (tc .input ,COMMON_PEG_PARSE_FLAG_LENIENT ); 191auto result = parser .parse (ctx ); 192 193// Assert result type matches 194assert_result_equal (t ,tc .expected_result ,result .type ); 195 196// Assert matched text if success or need_more_input 197if (result .success ()|| result .need_more_input ()) { 198 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 199t .assert_equal (tc .expected_text ,matched ); 200 } 201 }); 202 } 203 }); 204 }); 205 206t .test ("until parser" , [](testing & t ) { 207t .test ("ASCII delimiter with Unicode content" , [](testing & t ) { 208 std::vector < test_case > test_cases { 209// CJK characters before delimiter 210 {std::string ("\xE4\xBD\xA0\xE5\xA5\xBD</tag>" ), std::string ("\xE4\xBD\xA0\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 211 212// Emoji before delimiter 213 {std::string ("\xF0\x9F\x98\x80</tag>" ), std::string ("\xF0\x9F\x98\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 214 215// Mixed content 216 {std::string ("Hello \xE4\xB8\x96\xE7\x95\x8C!</tag>" ), std::string ("Hello \xE4\xB8\x96\xE7\x95\x8C!" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 217 }; 218 219auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 220return p .until ("</tag>" ); 221 }); 222 223for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 224const auto & tc = test_cases [i ]; 225 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 226 227t .test (test_name , [& ](testing & t ) { 228common_peg_parse_context ctx (tc .input ); 229auto result = parser .parse (ctx ); 230 231assert_result_equal (t ,tc .expected_result ,result .type ); 232 233if (result .success ()) { 234 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 235t .assert_equal (tc .expected_text ,matched ); 236 } 237 }); 238 } 239 }); 240 241t .test ("incomplete UTF-8 at end" , [](testing & t ) { 242 std::vector < test_case > test_cases { 243// Incomplete emoji at end, no delimiter 244 {std::string ("content\xF0\x9F\x98" ), std::string ("content" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 245 246// Incomplete CJK at end, no delimiter 247 {std::string ("hello\xE4\xB8" ), std::string ("hello" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 248 249// Complete content, no delimiter (should consume all valid UTF-8) 250 {std::string ("\xE4\xBD\xA0\xE5\xA5\xBD" ), std::string ("\xE4\xBD\xA0\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 251 }; 252 253auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 254return p .until ("</tag>" ); 255 }); 256 257for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 258const auto & tc = test_cases [i ]; 259 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 260 261t .test (test_name , [& ](testing & t ) { 262common_peg_parse_context ctx (tc .input ,COMMON_PEG_PARSE_FLAG_LENIENT ); 263auto result = parser .parse (ctx ); 264 265assert_result_equal (t ,tc .expected_result ,result .type ); 266 267if (result .success ()|| result .need_more_input ()) { 268 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 269t .assert_equal (tc .expected_text ,matched ); 270 } 271 }); 272 } 273 }); 274 275t .test ("malformed UTF-8" , [](testing & t ) { 276 std::vector < test_case > test_cases { 277// Invalid UTF-8 bytes 278 {std::string ("Hello\xFF\xFE" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 279 280// Continuation byte without lead byte 281 {std::string ("Hello\x80World" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 282 283// Invalid continuation byte 284 {std::string ("\xC3\x28" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 285 }; 286 287auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 288return p .until ("</tag>" ); 289 }); 290 291for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 292const auto & tc = test_cases [i ]; 293 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 294 295t .test (test_name , [& ](testing & t ) { 296common_peg_parse_context ctx (tc .input ); 297auto result = parser .parse (ctx ); 298 299assert_result_equal (t ,tc .expected_result ,result .type ); 300 }); 301 } 302 }); 303 }); 304 305t .test ("json_string parser" , [](testing & t ) { 306t .test ("valid UTF-8 characters" , [](testing & t ) { 307 std::vector < test_case > test_cases { 308// ASCII only 309 {"Hello World\"" ,"Hello World" ,COMMON_PEG_PARSE_RESULT_SUCCESS }, 310 311// 2-byte UTF-8 (accented characters) 312 {std::string ("Caf\xC3\xA9\"" ), std::string ("Caf\xC3\xA9" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 313 314// 3-byte UTF-8 (CJK) 315 {std::string ("\xE4\xBD\xA0\xE5\xA5\xBD\"" ), std::string ("\xE4\xBD\xA0\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 316 317// 4-byte UTF-8 (emoji) 318 {std::string ("\xF0\x9F\x98\x80\"" ), std::string ("\xF0\x9F\x98\x80" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 319 320// Mixed content 321 {std::string ("Hello \xE4\xB8\x96\xE7\x95\x8C!\"" ), std::string ("Hello \xE4\xB8\x96\xE7\x95\x8C!" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 322 }; 323 324for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 325const auto & tc = test_cases [i ]; 326 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 327 328t .test (test_name , [& ](testing & t ) { 329auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 330return p .sequence ({p .string_content ('"' ),p .literal ("\"" )}); 331 }); 332 333common_peg_parse_context ctx (tc .input ); 334auto result = parser .parse (ctx ); 335 336assert_result_equal (t ,tc .expected_result ,result .type ); 337 338if (result .success ()) { 339 std::string matched = tc .input .substr (result .start ,result .end - result .start - 1 );// -1 to exclude closing quote 340t .assert_equal (tc .expected_text ,matched ); 341 } 342 }); 343 } 344 }); 345 346t .test ("incomplete UTF-8" , [](testing & t ) { 347 std::vector < test_case > test_cases { 348// Incomplete 2-byte sequence 349 {std::string ("Caf\xC3" ), std::string ("Caf" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 350 351// Incomplete 3-byte sequence 352 {std::string ("Hello\xE4\xB8" ), std::string ("Hello" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 353 354// Incomplete 4-byte sequence 355 {std::string ("Text\xF0\x9F\x98" ), std::string ("Text" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 356 357// Incomplete at very start 358 {std::string ("\xE4\xBD" ), std::string ("" ),COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT }, 359 }; 360 361for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 362const auto & tc = test_cases [i ]; 363 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 364 365t .test (test_name , [& ](testing & t ) { 366auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 367return p .string_content ('"' ); 368 }); 369 370common_peg_parse_context ctx (tc .input ,COMMON_PEG_PARSE_FLAG_LENIENT ); 371auto result = parser .parse (ctx ); 372 373assert_result_equal (t ,tc .expected_result ,result .type ); 374 375if (result .need_more_input ()) { 376 std::string matched = tc .input .substr (result .start ,result .end - result .start ); 377t .assert_equal (tc .expected_text ,matched ); 378 } 379 }); 380 } 381 }); 382 383t .test ("malformed UTF-8" , [](testing & t ) { 384 std::vector < test_case > test_cases { 385// Invalid UTF-8 bytes 386 {std::string ("Hello\xFF\xFE" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 387 388// Continuation byte without lead byte 389 {std::string ("Hello\x80World" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 390 391// Invalid continuation byte 392 {std::string ("\xC3\x28" ),"" ,COMMON_PEG_PARSE_RESULT_FAIL }, 393 }; 394 395for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 396const auto & tc = test_cases [i ]; 397 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 398 399t .test (test_name , [& ](testing & t ) { 400auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 401return p .string_content ('"' ); 402 }); 403 404common_peg_parse_context ctx (tc .input ); 405auto result = parser .parse (ctx ); 406 407assert_result_equal (t ,tc .expected_result ,result .type ); 408 }); 409 } 410 }); 411 412t .test ("escape sequences with UTF-8" , [](testing & t ) { 413 std::vector < test_case > test_cases { 414// Unicode escape sequence 415 {"Hello\\u0041\"" ,"Hello\\u0041" ,COMMON_PEG_PARSE_RESULT_SUCCESS }, 416 417// Mix of UTF-8 and escape sequences 418 {std::string ("\xE4\xBD\xA0\\n\xE5\xA5\xBD\"" ), std::string ("\xE4\xBD\xA0\\n\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 419 420// Escaped quote in UTF-8 string 421 {std::string ("\xE4\xBD\xA0\\\"\xE5\xA5\xBD\"" ), std::string ("\xE4\xBD\xA0\\\"\xE5\xA5\xBD" ),COMMON_PEG_PARSE_RESULT_SUCCESS }, 422 }; 423 424for (size_t i = 0 ;i < test_cases .size ();i ++ ) { 425const auto & tc = test_cases [i ]; 426 std::string test_name = "case " + std::to_string (i )+ ": " + hex_dump (tc .input ); 427 428t .test (test_name , [& ](testing & t ) { 429auto parser = build_peg_parser ([](common_peg_parser_builder & p ) { 430return p .sequence ({p .string_content ('"' ),p .literal ("\"" )}); 431 }); 432 433common_peg_parse_context ctx (tc .input ); 434auto result = parser .parse (ctx ); 435 436assert_result_equal (t ,tc .expected_result ,result .type ); 437 438if (result .success ()) { 439 std::string matched = tc .input .substr (result .start ,result .end - result .start - 1 );// -1 to exclude closing quote 440t .assert_equal (tc .expected_text ,matched ); 441 } 442 }); 443 } 444 }); 445 }); 446}