char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
acecd5603
char/aliceai-t5-moe
1#ifdef NDEBUG 2#undef NDEBUG 3#endif 4 5#include "json-schema-to-grammar.h" 6 7#include "../src/llama-grammar.h" 8 9#include "json.h" 10 11#include <cassert> 12#include <regex> 13 14static std::string trim (const std::string & source ) { 15 std::string s (source ); 16s .erase (0 ,s .find_first_not_of (" \n\r\t" )); 17s .erase (s .find_last_not_of (" \n\r\t" )+ 1 ); 18return std::regex_replace (s , std::regex ("(^|\n)[ \t]+" ),"$1" ); 19} 20 21enum TestCaseStatus { 22SUCCESS , 23FAILURE 24}; 25 26struct TestCase { 27TestCaseStatus expected_status ; 28 std::string name ; 29 std::string schema ; 30 std::string expected_grammar ; 31 32void _print_failure_header ()const { 33fprintf (stderr ,"#\n# Test '%s' failed.\n#\n%s\n" ,name .c_str (),schema .c_str ()); 34 } 35void verify (const std::string & actual_grammar )const { 36if (trim (actual_grammar )!= trim (expected_grammar )) { 37_print_failure_header (); 38fprintf (stderr ,"# EXPECTED:\n%s\n# ACTUAL:\n%s\n" ,expected_grammar .c_str (),actual_grammar .c_str ()); 39assert (false); 40 } 41 } 42void verify_expectation_parseable ()const { 43try { 44llama_grammar_parser state ; 45state .parse (expected_grammar .c_str ()); 46if (state .symbol_ids .find ("root" )== state .symbol_ids .end ()) { 47throw std::runtime_error ("Grammar failed to parse:\n" + expected_grammar ); 48 } 49 }catch (const std::runtime_error & ex ) { 50_print_failure_header (); 51fprintf (stderr ,"# GRAMMAR ERROR: %s\n" ,ex .what ()); 52assert (false); 53 } 54 } 55void verify_status (TestCaseStatus status )const { 56if (status != expected_status ) { 57_print_failure_header (); 58fprintf (stderr ,"# EXPECTED STATUS: %s\n" ,expected_status == SUCCESS ?"SUCCESS" :"FAILURE" ); 59fprintf (stderr ,"# ACTUAL STATUS: %s\n" ,status == SUCCESS ?"SUCCESS" :"FAILURE" ); 60assert (false); 61 } 62 } 63}; 64 65static void test_all (const std::string & title , std::function < void (const TestCase & )> runner ) { 66fprintf (stderr ,"#\n# %s\n#\n" ,title .c_str ()); 67auto test = [& ](const TestCase & tc ) { 68fprintf (stderr ,"- %s%s\n" ,tc .name .c_str (),tc .expected_status == FAILURE ?" (failure expected)" :"" ); 69runner (tc ); 70 }; 71 72test ({ 73SUCCESS , 74"min 0" , 75R"""({ 76"type": "integer", 77"minimum": 0 78})""" , 79R"""( 80root ::= ([0] | [1-9] [0-9]{0,15}) 81space ::= | " " | "\n"{1,2} [ \t]{0,20} 82)""" 83 }); 84 85test ({ 86SUCCESS , 87"min 1" , 88R"""({ 89"type": "integer", 90"minimum": 1 91})""" , 92R"""( 93root ::= ([1-9] [0-9]{0,15}) 94space ::= | " " | "\n"{1,2} [ \t]{0,20} 95)""" 96 }); 97 98test ({ 99SUCCESS , 100"min 3" , 101R"""({ 102"type": "integer", 103"minimum": 3 104})""" , 105R"""( 106root ::= ([1-2] [0-9]{1,15} | [3-9] [0-9]{0,15}) 107space ::= | " " | "\n"{1,2} [ \t]{0,20} 108)""" 109 }); 110 111test ({ 112SUCCESS , 113"min 9" , 114R"""({ 115"type": "integer", 116"minimum": 9 117})""" , 118R"""( 119root ::= ([1-8] [0-9]{1,15} | [9] [0-9]{0,15}) 120space ::= | " " | "\n"{1,2} [ \t]{0,20} 121)""" 122 }); 123 124test ({ 125SUCCESS , 126"min 10" , 127R"""({ 128"type": "integer", 129"minimum": 10 130})""" , 131R"""( 132root ::= ([1] ([0-9]{1,15}) | [2-9] [0-9]{1,15}) 133space ::= | " " | "\n"{1,2} [ \t]{0,20} 134)""" 135 }); 136 137test ({ 138SUCCESS , 139"min 25" , 140R"""({ 141"type": "integer", 142"minimum": 25 143})""" , 144R"""( 145root ::= ([1] [0-9]{2,15} | [2] ([0-4] [0-9]{1,14} | [5-9] [0-9]{0,14}) | [3-9] [0-9]{1,15}) 146space ::= | " " | "\n"{1,2} [ \t]{0,20} 147)""" 148 }); 149 150test ({ 151SUCCESS , 152"max 30" , 153R"""({ 154"type": "integer", 155"maximum": 30 156})""" , 157R"""( 158root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-2] [0-9] | [3] "0")) 159space ::= | " " | "\n"{1,2} [ \t]{0,20} 160)""" 161 }); 162 163test ({ 164SUCCESS , 165"min -5" , 166R"""({ 167"type": "integer", 168"minimum": -5 169})""" , 170R"""( 171root ::= ("-" ([0-5]) | [0] | [1-9] [0-9]{0,15}) 172space ::= | " " | "\n"{1,2} [ \t]{0,20} 173)""" 174 }); 175 176test ({ 177SUCCESS , 178"min -123" , 179R"""({ 180"type": "integer", 181"minimum": -123 182})""" , 183R"""( 184root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0] | [1-9] [0-9]{0,15}) 185space ::= | " " | "\n"{1,2} [ \t]{0,20} 186)""" 187 }); 188 189test ({ 190SUCCESS , 191"max -5" , 192R"""({ 193"type": "integer", 194"maximum": -5 195})""" , 196R"""( 197root ::= ("-" ([0-4] [0-9]{1,15} | [5-9] [0-9]{0,15})) 198space ::= | " " | "\n"{1,2} [ \t]{0,20} 199)""" 200 }); 201 202test ({ 203SUCCESS , 204"max 1" , 205R"""({ 206"type": "integer", 207"maximum": 1 208})""" , 209R"""( 210root ::= ("-" [1-9] [0-9]{0,15} | [0-1]) 211space ::= | " " | "\n"{1,2} [ \t]{0,20} 212)""" 213 }); 214 215test ({ 216SUCCESS , 217"max 100" , 218R"""({ 219"type": "integer", 220"maximum": 100 221})""" , 222R"""( 223root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-8] [0-9] | [9] [0-9]) | "100") 224space ::= | " " | "\n"{1,2} [ \t]{0,20} 225)""" 226 }); 227 228test ({ 229SUCCESS , 230"min 0 max 23" , 231R"""({ 232"type": "integer", 233"minimum": 0, 234"maximum": 23 235})""" , 236R"""( 237root ::= ([0-9] | ([1] [0-9] | [2] [0-3])) 238space ::= | " " | "\n"{1,2} [ \t]{0,20} 239)""" 240 }); 241 242test ({ 243SUCCESS , 244"min 15 max 300" , 245R"""({ 246"type": "integer", 247"minimum": 15, 248"maximum": 300 249})""" , 250R"""( 251root ::= (([1] ([5-9]) | [2-9] [0-9]) | ([1-2] [0-9]{2} | [3] "00")) 252space ::= | " " | "\n"{1,2} [ \t]{0,20} 253)""" 254 }); 255 256test ({ 257SUCCESS , 258"min 5 max 30" , 259R"""({ 260"type": "integer", 261"minimum": 5, 262"maximum": 30 263})""" , 264R"""( 265root ::= ([5-9] | ([1-2] [0-9] | [3] "0")) 266space ::= | " " | "\n"{1,2} [ \t]{0,20} 267)""" 268 }); 269 270test ({ 271SUCCESS , 272"min -123 max 42" , 273R"""({ 274"type": "integer", 275"minimum": -123, 276"maximum": 42 277})""" , 278R"""( 279root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0-9] | ([1-3] [0-9] | [4] [0-2])) 280space ::= | " " | "\n"{1,2} [ \t]{0,20} 281)""" 282 }); 283 284test ({ 285SUCCESS , 286"min -10 max 10" , 287R"""({ 288"type": "integer", 289"minimum": -10, 290"maximum": 10 291})""" , 292R"""( 293root ::= ("-" ([0-9] | "10") | [0-9] | "10") 294space ::= | " " | "\n"{1,2} [ \t]{0,20} 295)""" 296 }); 297 298test ({ 299FAILURE , 300"unknown type" , 301R"""({ 302"type": "kaboom" 303})""" , 304"" 305 }); 306 307test ({ 308FAILURE , 309"invalid type" , 310R"""({ 311"type": 123 312})""" , 313"" 314 }); 315 316test ({ 317SUCCESS , 318"empty schema (any value)" , 319"{}" , 320R"""( 321array ::= "[" space ( value ("," space value)* )? space "]" 322boolean ::= ("true" | "false") 323char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 324decimal-part ::= [0-9]{1,16} 325integral-part ::= [0] | [1-9] [0-9]{0,15} 326null ::= "null" 327number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 328object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? space "}" 329root ::= value 330space ::= | " " | "\n"{1,2} [ \t]{0,20} 331string ::= "\"" char* "\"" 332value ::= object | array | string | number | boolean | null 333)""" 334 }); 335 336test ({ 337SUCCESS , 338"exotic formats" , 339R"""({ 340"items": [ 341{ "format": "date" }, 342{ "format": "uuid" }, 343{ "format": "time" }, 344{ "format": "date-time" } 345] 346})""" , 347R"""( 348date ::= [0-9]{4} "-" ( "0" [1-9] | "1" [0-2] ) "-" ( "0" [1-9] | [1-2] [0-9] | "3" [0-1] ) 349date-string ::= "\"" date "\"" 350date-time ::= date "T" time 351date-time-string ::= "\"" date-time "\"" 352root ::= "[" space tuple-0 "," space uuid "," space tuple-2 "," space tuple-3 space "]" 353space ::= | " " | "\n"{1,2} [ \t]{0,20} 354time ::= ([01] [0-9] | "2" [0-3]) ":" [0-5] [0-9] ":" [0-5] [0-9] ( "." [0-9]{3} )? ( "Z" | ( "+" | "-" ) ( [01] [0-9] | "2" [0-3] ) ":" [0-5] [0-9] ) 355time-string ::= "\"" time "\"" 356tuple-0 ::= date-string 357tuple-2 ::= time-string 358tuple-3 ::= date-time-string 359uuid ::= "\"" [0-9a-fA-F]{8} "-" [0-9a-fA-F]{4} "-" [0-9a-fA-F]{4} "-" [0-9a-fA-F]{4} "-" [0-9a-fA-F]{12} "\"" 360)""" 361 }); 362 363test ({ 364SUCCESS , 365"string" , 366R"""({ 367"type": "string" 368})""" , 369R"""( 370char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 371root ::= "\"" char* "\"" 372space ::= | " " | "\n"{1,2} [ \t]{0,20} 373)""" 374 }); 375 376test ({ 377SUCCESS , 378"string w/ min length 1" , 379R"""({ 380"type": "string", 381"minLength": 1 382})""" , 383R"""( 384char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 385root ::= "\"" char+ "\"" 386space ::= | " " | "\n"{1,2} [ \t]{0,20} 387)""" 388 }); 389 390test ({ 391SUCCESS , 392"string w/ min length 3" , 393R"""({ 394"type": "string", 395"minLength": 3 396})""" , 397R"""( 398char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 399root ::= "\"" char{3,} "\"" 400space ::= | " " | "\n"{1,2} [ \t]{0,20} 401)""" 402 }); 403 404test ({ 405SUCCESS , 406"string w/ max length" , 407R"""({ 408"type": "string", 409"maxLength": 3 410})""" , 411R"""( 412char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 413root ::= "\"" char{0,3} "\"" 414space ::= | " " | "\n"{1,2} [ \t]{0,20} 415)""" 416 }); 417 418test ({ 419SUCCESS , 420"string w/ min & max length" , 421R"""({ 422"type": "string", 423"minLength": 1, 424"maxLength": 4 425})""" , 426R"""( 427char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 428root ::= "\"" char{1,4} "\"" 429space ::= | " " | "\n"{1,2} [ \t]{0,20} 430)""" 431 }); 432 433test ({ 434SUCCESS , 435"boolean" , 436R"""({ 437"type": "boolean" 438})""" , 439R"""( 440root ::= ("true" | "false") 441space ::= | " " | "\n"{1,2} [ \t]{0,20} 442)""" 443 }); 444 445test ({ 446SUCCESS , 447"integer" , 448R"""({ 449"type": "integer" 450})""" , 451R"""( 452integral-part ::= [0] | [1-9] [0-9]{0,15} 453root ::= ("-"? integral-part) 454space ::= | " " | "\n"{1,2} [ \t]{0,20} 455)""" 456 }); 457 458test ({ 459SUCCESS , 460"string const" , 461R"""({ 462"const": "foo" 463})""" , 464R"""( 465root ::= "\"foo\"" 466space ::= | " " | "\n"{1,2} [ \t]{0,20} 467)""" 468 }); 469 470test ({ 471SUCCESS , 472"non-string const" , 473R"""({ 474"const": 123 475})""" , 476R"""( 477root ::= "123" 478space ::= | " " | "\n"{1,2} [ \t]{0,20} 479)""" 480 }); 481 482test ({ 483SUCCESS , 484"non-string enum" , 485R"""({ 486"enum": ["red", "amber", "green", null, 42, ["foo"]] 487})""" , 488R"""( 489root ::= ("\"red\"" | "\"amber\"" | "\"green\"" | "null" | "42" | "[\"foo\"]") 490space ::= | " " | "\n"{1,2} [ \t]{0,20} 491)""" 492 }); 493 494test ({ 495SUCCESS , 496"string array" , 497R"""({ 498"type": "array", 499"prefixItems": { "type": "string" } 500})""" , 501R"""( 502char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 503root ::= "[" space (string ("," space string)*)? space "]" 504space ::= | " " | "\n"{1,2} [ \t]{0,20} 505string ::= "\"" char* "\"" 506)""" 507 }); 508 509test ({ 510SUCCESS , 511"nullable string array" , 512R"""({ 513"type": ["array", "null"], 514"prefixItems": { "type": "string" } 515})""" , 516R"""( 517alternative-0 ::= "[" space (string ("," space string)*)? space "]" 518char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 519null ::= "null" 520root ::= alternative-0 | null 521space ::= | " " | "\n"{1,2} [ \t]{0,20} 522string ::= "\"" char* "\"" 523)""" 524 }); 525 526test ({ 527SUCCESS , 528"tuple1" , 529R"""({ 530"prefixItems": [{ "type": "string" }] 531})""" , 532R"""( 533char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 534root ::= "[" space string space "]" 535space ::= | " " | "\n"{1,2} [ \t]{0,20} 536string ::= "\"" char* "\"" 537)""" 538 }); 539 540test ({ 541SUCCESS , 542"tuple2" , 543R"""({ 544"prefixItems": [{ "type": "string" }, { "type": "number" }] 545})""" , 546R"""( 547char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 548decimal-part ::= [0-9]{1,16} 549integral-part ::= [0] | [1-9] [0-9]{0,15} 550number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 551root ::= "[" space string "," space number space "]" 552space ::= | " " | "\n"{1,2} [ \t]{0,20} 553string ::= "\"" char* "\"" 554)""" 555 }); 556 557// items {} constrains nothing, the same as no items at all 558test ({ 559SUCCESS , 560"array with empty items" , 561R"""({ 562"type": "array", 563"items": {} 564})""" , 565R"""( 566array ::= "[" space ( value ("," space value)* )? space "]" 567boolean ::= ("true" | "false") 568char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 569decimal-part ::= [0-9]{1,16} 570integral-part ::= [0] | [1-9] [0-9]{0,15} 571null ::= "null" 572number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 573object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? space "}" 574root ::= "[" space ( value ("," space value)* )? space "]" 575space ::= | " " | "\n"{1,2} [ \t]{0,20} 576string ::= "\"" char* "\"" 577value ::= object | array | string | number | boolean | null 578)""" 579 }); 580 581test ({ 582SUCCESS , 583"array with empty items and prefixItems" , 584R"""({ 585"type": "array", 586"items": {}, 587"prefixItems": { "type": "string" } 588})""" , 589R"""( 590array ::= "[" space ( value ("," space value)* )? space "]" 591boolean ::= ("true" | "false") 592char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 593decimal-part ::= [0-9]{1,16} 594integral-part ::= [0] | [1-9] [0-9]{0,15} 595null ::= "null" 596number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 597object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? space "}" 598root ::= "[" space ( value ("," space value)* )? space "]" 599space ::= | " " | "\n"{1,2} [ \t]{0,20} 600string ::= "\"" char* "\"" 601value ::= object | array | string | number | boolean | null 602)""" 603 }); 604 605test ({ 606SUCCESS , 607"number" , 608R"""({ 609"type": "number" 610})""" , 611R"""( 612decimal-part ::= [0-9]{1,16} 613integral-part ::= [0] | [1-9] [0-9]{0,15} 614root ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 615space ::= | " " | "\n"{1,2} [ \t]{0,20} 616)""" 617 }); 618 619test ({ 620SUCCESS , 621"minItems" , 622R"""({ 623"items": { 624"type": "boolean" 625}, 626"minItems": 2 627})""" , 628R"""( 629boolean ::= ("true" | "false") 630root ::= "[" space boolean ("," space boolean)+ space "]" 631space ::= | " " | "\n"{1,2} [ \t]{0,20} 632)""" 633 }); 634 635test ({ 636SUCCESS , 637"maxItems 0" , 638R"""({ 639"items": { 640"type": "boolean" 641}, 642"maxItems": 0 643})""" , 644R"""( 645boolean ::= ("true" | "false") 646root ::= "[" space space "]" 647space ::= | " " | "\n"{1,2} [ \t]{0,20} 648)""" 649 }); 650 651test ({ 652SUCCESS , 653"maxItems 1" , 654R"""({ 655"items": { 656"type": "boolean" 657}, 658"maxItems": 1 659})""" , 660R"""( 661boolean ::= ("true" | "false") 662root ::= "[" space boolean? space "]" 663space ::= | " " | "\n"{1,2} [ \t]{0,20} 664)""" 665 }); 666 667test ({ 668SUCCESS , 669"maxItems 2" , 670R"""({ 671"items": { 672"type": "boolean" 673}, 674"maxItems": 2 675})""" , 676R"""( 677boolean ::= ("true" | "false") 678root ::= "[" space (boolean ("," space boolean)?)? space "]" 679space ::= | " " | "\n"{1,2} [ \t]{0,20} 680)""" 681 }); 682 683test ({ 684SUCCESS , 685"min + maxItems" , 686R"""({ 687"items": { 688"type": ["number", "integer"] 689}, 690"minItems": 3, 691"maxItems": 5 692})""" , 693R"""( 694decimal-part ::= [0-9]{1,16} 695integer ::= ("-"? integral-part) 696integral-part ::= [0] | [1-9] [0-9]{0,15} 697item ::= number | integer 698number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 699root ::= "[" space item ("," space item){2,4} space "]" 700space ::= | " " | "\n"{1,2} [ \t]{0,20} 701)""" 702 }); 703 704test ({ 705SUCCESS , 706"min + max items with min + max values across zero" , 707R"""({ 708"items": { 709"type": "integer", 710"minimum": -12, 711"maximum": 207 712}, 713"minItems": 3, 714"maxItems": 5 715})""" , 716R"""( 717item ::= ("-" ([0-9] | "1" [0-2]) | [0-9] | ([1-8] [0-9] | [9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) 718root ::= "[" space item ("," space item){2,4} space "]" 719space ::= | " " | "\n"{1,2} [ \t]{0,20} 720)""" 721 }); 722 723test ({ 724SUCCESS , 725"min + max items with min + max values" , 726R"""({ 727"items": { 728"type": "integer", 729"minimum": 12, 730"maximum": 207 731}, 732"minItems": 3, 733"maxItems": 5 734})""" , 735R"""( 736item ::= (([1] ([2-9]) | [2-9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) 737root ::= "[" space item ("," space item){2,4} space "]" 738space ::= | " " | "\n"{1,2} [ \t]{0,20} 739)""" 740 }); 741 742test ({ 743SUCCESS , 744"simple regexp" , 745R"""({ 746"type": "string", 747"pattern": "^abc?d*efg+(hij)?kl$" 748})""" , 749R"""( 750root ::= "\"" ("ab" "c"? "d"* "ef" "g"+ ("hij")? "kl") "\"" 751space ::= | " " | "\n"{1,2} [ \t]{0,20} 752)""" 753 }); 754 755test ({ 756SUCCESS , 757"regexp escapes" , 758R"""({ 759"type": "string", 760"pattern": "^\\[\\]\\{\\}\\(\\)\\|\\+\\*\\?$" 761})""" , 762R"""( 763root ::= "\"" ("[]{}()|+*?") "\"" 764space ::= | " " | "\n"{1,2} [ \t]{0,20} 765)""" 766 }); 767 768test ({ 769SUCCESS , 770"regexp quote" , 771R"""({ 772"type": "string", 773"pattern": "^\"$" 774})""" , 775R"""( 776root ::= "\"" ("\"") "\"" 777space ::= | " " | "\n"{1,2} [ \t]{0,20} 778)""" 779 }); 780 781test ({ 782SUCCESS , 783"regexp with top-level alternation" , 784R"""({ 785"type": "string", 786"pattern": "^A|B|C|D$" 787})""" , 788R"""( 789root ::= "\"" ("A" | "B" | "C" | "D") "\"" 790space ::= | " " | "\n"{1,2} [ \t]{0,20} 791)""" 792 }); 793 794test ({ 795SUCCESS , 796"regexp" , 797R"""({ 798"type": "string", 799"pattern": "^(\\([0-9]{1,3}\\))?[0-9]{3}-[0-9]{4} a{3,5}nd...$" 800})""" , 801R"""( 802dot ::= [^\x0A\x0D] 803root ::= "\"" (("(" root-1{1,3} ")")? root-1{3,3} "-" root-1{4,4} " " "a"{3,5} "nd" dot dot dot) "\"" 804root-1 ::= [0-9] 805space ::= | " " | "\n"{1,2} [ \t]{0,20} 806)""" 807 }); 808 809test ({ 810SUCCESS , 811"required props in original order" , 812R"""({ 813"type": "object", 814"properties": { 815"b": {"type": "string"}, 816"c": {"type": "string"}, 817"a": {"type": "string"} 818}, 819"required": [ 820"a", 821"b", 822"c" 823], 824"additionalProperties": false, 825"definitions": {} 826})""" , 827R"""( 828a-kv ::= "\"a\"" space ":" space string 829b-kv ::= "\"b\"" space ":" space string 830c-kv ::= "\"c\"" space ":" space string 831char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 832root ::= "{" space b-kv "," space c-kv "," space a-kv space "}" 833space ::= | " " | "\n"{1,2} [ \t]{0,20} 834string ::= "\"" char* "\"" 835)""" 836 }); 837 838test ({ 839SUCCESS , 840"1 optional prop" , 841R"""({ 842"properties": { 843"a": { 844"type": "string" 845} 846}, 847"additionalProperties": false 848})""" , 849R"""( 850a-kv ::= "\"a\"" space ":" space string 851char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 852root ::= "{" space (a-kv )? space "}" 853space ::= | " " | "\n"{1,2} [ \t]{0,20} 854string ::= "\"" char* "\"" 855)""" 856 }); 857 858test ({ 859SUCCESS , 860"N optional props" , 861R"""({ 862"properties": { 863"a": {"type": "string"}, 864"b": {"type": "string"}, 865"c": {"type": "string"} 866}, 867"additionalProperties": false 868})""" , 869R"""( 870a-kv ::= "\"a\"" space ":" space string 871a-rest ::= ( "," space b-kv )? b-rest 872b-kv ::= "\"b\"" space ":" space string 873b-rest ::= ( "," space c-kv )? 874c-kv ::= "\"c\"" space ":" space string 875char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 876root ::= "{" space (a-kv a-rest | b-kv b-rest | c-kv )? space "}" 877space ::= | " " | "\n"{1,2} [ \t]{0,20} 878string ::= "\"" char* "\"" 879)""" 880 }); 881 882test ({ 883SUCCESS , 884"required + optional props each in original order" , 885R"""({ 886"properties": { 887"b": {"type": "string"}, 888"a": {"type": "string"}, 889"d": {"type": "string"}, 890"c": {"type": "string"} 891}, 892"required": ["a", "b"], 893"additionalProperties": false 894})""" , 895R"""( 896a-kv ::= "\"a\"" space ":" space string 897b-kv ::= "\"b\"" space ":" space string 898c-kv ::= "\"c\"" space ":" space string 899char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 900d-kv ::= "\"d\"" space ":" space string 901d-rest ::= ( "," space c-kv )? 902root ::= "{" space b-kv "," space a-kv ( "," space ( d-kv d-rest | c-kv ) )? space "}" 903space ::= | " " | "\n"{1,2} [ \t]{0,20} 904string ::= "\"" char* "\"" 905)""" 906 }); 907 908test ({ 909SUCCESS , 910"additional props" , 911R"""({ 912"type": "object", 913"additionalProperties": {"type": "array", "items": {"type": "number"}} 914})""" , 915R"""( 916additional-kv ::= string ":" space additional-value 917additional-value ::= "[" space (number ("," space number)*)? space "]" 918char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 919decimal-part ::= [0-9]{1,16} 920integral-part ::= [0] | [1-9] [0-9]{0,15} 921number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 922root ::= "{" space (additional-kv ( "," space additional-kv )* )? space "}" 923space ::= | " " | "\n"{1,2} [ \t]{0,20} 924string ::= "\"" char* "\"" 925)""" 926 }); 927 928test ({ 929SUCCESS , 930"additional props (true)" , 931R"""({ 932"type": "object", 933"additionalProperties": true 934})""" , 935R"""( 936array ::= "[" space ( value ("," space value)* )? space "]" 937boolean ::= ("true" | "false") 938char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 939decimal-part ::= [0-9]{1,16} 940integral-part ::= [0] | [1-9] [0-9]{0,15} 941null ::= "null" 942number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 943object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? space "}" 944root ::= object 945space ::= | " " | "\n"{1,2} [ \t]{0,20} 946string ::= "\"" char* "\"" 947value ::= object | array | string | number | boolean | null 948)""" 949 }); 950 951test ({ 952SUCCESS , 953"additional props (implicit)" , 954R"""({ 955"type": "object" 956})""" , 957R"""( 958array ::= "[" space ( value ("," space value)* )? space "]" 959boolean ::= ("true" | "false") 960char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 961decimal-part ::= [0-9]{1,16} 962integral-part ::= [0] | [1-9] [0-9]{0,15} 963null ::= "null" 964number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 965object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? space "}" 966root ::= object 967space ::= | " " | "\n"{1,2} [ \t]{0,20} 968string ::= "\"" char* "\"" 969value ::= object | array | string | number | boolean | null 970)""" 971 }); 972 973test ({ 974SUCCESS , 975"empty w/o additional props" , 976R"""({ 977"type": "object", 978"additionalProperties": false 979})""" , 980R"""( 981root ::= "{" space "}" 982space ::= | " " | "\n"{1,2} [ \t]{0,20} 983)""" 984 }); 985 986test ({ 987SUCCESS , 988"required + additional props" , 989R"""({ 990"type": "object", 991"properties": { 992"a": {"type": "number"} 993}, 994"required": ["a"], 995"additionalProperties": {"type": "string"} 996})""" , 997R"""( 998a-kv ::= "\"a\"" space ":" space number 999additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] 1000additional-kv ::= additional-k ":" space string 1001char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1002decimal-part ::= [0-9]{1,16} 1003integral-part ::= [0] | [1-9] [0-9]{0,15} 1004number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1005root ::= "{" space a-kv ( "," space ( additional-kv ( "," space additional-kv )* ) )? space "}" 1006space ::= | " " | "\n"{1,2} [ \t]{0,20} 1007string ::= "\"" char* "\"" 1008)""" 1009 }); 1010 1011test ({ 1012SUCCESS , 1013"optional + additional props" , 1014R"""({ 1015"type": "object", 1016"properties": { 1017"a": {"type": "number"} 1018}, 1019"additionalProperties": {"type": "number"} 1020})""" , 1021R"""( 1022a-kv ::= "\"a\"" space ":" space number 1023a-rest ::= ( "," space additional-kv )* 1024additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] 1025additional-kv ::= additional-k ":" space number 1026char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1027decimal-part ::= [0-9]{1,16} 1028integral-part ::= [0] | [1-9] [0-9]{0,15} 1029number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1030root ::= "{" space (a-kv a-rest | additional-kv ( "," space additional-kv )* )? space "}" 1031space ::= | " " | "\n"{1,2} [ \t]{0,20} 1032)""" 1033 }); 1034 1035test ({ 1036SUCCESS , 1037"required + optional + additional props" , 1038R"""({ 1039"type": "object", 1040"properties": { 1041"and": {"type": "number"}, 1042"also": {"type": "number"} 1043}, 1044"required": ["and"], 1045"additionalProperties": {"type": "number"} 1046})""" , 1047R"""( 1048additional-k ::= ["] ( [a] ([l] ([s] ([o] char+ | [^"o] char*) | [^"s] char*) | [n] ([d] char+ | [^"d] char*) | [^"ln] char*) | [^"a] char* )? ["] 1049additional-kv ::= additional-k ":" space number 1050also-kv ::= "\"also\"" space ":" space number 1051also-rest ::= ( "," space additional-kv )* 1052and-kv ::= "\"and\"" space ":" space number 1053char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1054decimal-part ::= [0-9]{1,16} 1055integral-part ::= [0] | [1-9] [0-9]{0,15} 1056number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1057root ::= "{" space and-kv ( "," space ( also-kv also-rest | additional-kv ( "," space additional-kv )* ) )? space "}" 1058space ::= | " " | "\n"{1,2} [ \t]{0,20} 1059)""" 1060 }); 1061 1062test ({ 1063SUCCESS , 1064"optional props with empty name" , 1065R"""({ 1066"properties": { 1067"": {"type": "integer"}, 1068"a": {"type": "integer"} 1069}, 1070"additionalProperties": {"type": "integer"} 1071})""" , 1072R"""( 1073-kv ::= "\"\"" space ":" space root 1074-rest ::= ( "," space a-kv )? a-rest 1075a-kv ::= "\"a\"" space ":" space integer 1076a-rest ::= ( "," space additional-kv )* 1077additional-k ::= ["] ( [a] char+ | [^"a] char* ) ["] 1078additional-kv ::= additional-k ":" space integer 1079char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1080integer ::= ("-"? integral-part) 1081integral-part ::= [0] | [1-9] [0-9]{0,15} 1082root ::= ("-"? integral-part) 1083root0 ::= "{" space (-kv -rest | a-kv a-rest | additional-kv ( "," space additional-kv )* )? space "}" 1084space ::= | " " | "\n"{1,2} [ \t]{0,20} 1085)""" 1086 }); 1087 1088test ({ 1089SUCCESS , 1090"optional props with nested names" , 1091R"""({ 1092"properties": { 1093"a": {"type": "integer"}, 1094"aa": {"type": "integer"} 1095}, 1096"additionalProperties": {"type": "integer"} 1097})""" , 1098R"""( 1099a-kv ::= "\"a\"" space ":" space integer 1100a-rest ::= ( "," space aa-kv )? aa-rest 1101aa-kv ::= "\"aa\"" space ":" space integer 1102aa-rest ::= ( "," space additional-kv )* 1103additional-k ::= ["] ( [a] ([a] char+ | [^"a] char*) | [^"a] char* )? ["] 1104additional-kv ::= additional-k ":" space integer 1105char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1106integer ::= ("-"? integral-part) 1107integral-part ::= [0] | [1-9] [0-9]{0,15} 1108root ::= "{" space (a-kv a-rest | aa-kv aa-rest | additional-kv ( "," space additional-kv )* )? space "}" 1109space ::= | " " | "\n"{1,2} [ \t]{0,20} 1110)""" 1111 }); 1112 1113test ({ 1114SUCCESS , 1115"optional props with common prefix" , 1116R"""({ 1117"properties": { 1118"ab": {"type": "integer"}, 1119"ac": {"type": "integer"} 1120}, 1121"additionalProperties": {"type": "integer"} 1122})""" , 1123R"""( 1124ab-kv ::= "\"ab\"" space ":" space integer 1125ab-rest ::= ( "," space ac-kv )? ac-rest 1126ac-kv ::= "\"ac\"" space ":" space integer 1127ac-rest ::= ( "," space additional-kv )* 1128additional-k ::= ["] ( [a] ([b] char+ | [c] char+ | [^"bc] char*) | [^"a] char* )? ["] 1129additional-kv ::= additional-k ":" space integer 1130char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1131integer ::= ("-"? integral-part) 1132integral-part ::= [0] | [1-9] [0-9]{0,15} 1133root ::= "{" space (ab-kv ab-rest | ac-kv ac-rest | additional-kv ( "," space additional-kv )* )? space "}" 1134space ::= | " " | "\n"{1,2} [ \t]{0,20} 1135)""" 1136 }); 1137 1138test ({ 1139SUCCESS , 1140"top-level $ref" , 1141R"""({ 1142"$ref": "#/definitions/foo", 1143"definitions": { 1144"foo": { 1145"type": "object", 1146"properties": { 1147"a": { 1148"type": "string" 1149} 1150}, 1151"required": [ 1152"a" 1153], 1154"additionalProperties": false 1155} 1156} 1157})""" , 1158R"""( 1159char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1160ref-definitions-foo ::= "{" space ref-definitions-foo-a-kv space "}" 1161ref-definitions-foo-a-kv ::= "\"a\"" space ":" space string 1162root ::= ref-definitions-foo 1163space ::= | " " | "\n"{1,2} [ \t]{0,20} 1164string ::= "\"" char* "\"" 1165)""" 1166 }); 1167 1168test ({ 1169SUCCESS , 1170"anyOf" , 1171R"""({ 1172"anyOf": [ 1173{"$ref": "#/definitions/foo"}, 1174{"$ref": "#/definitions/bar"} 1175], 1176"definitions": { 1177"foo": { 1178"properties": {"a": {"type": "number"}} 1179}, 1180"bar": { 1181"properties": {"b": {"type": "number"}} 1182} 1183}, 1184"type": "object" 1185})""" , 1186R"""( 1187alternative-0 ::= ref-definitions-foo 1188alternative-1 ::= ref-definitions-bar 1189decimal-part ::= [0-9]{1,16} 1190integral-part ::= [0] | [1-9] [0-9]{0,15} 1191number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1192ref-definitions-bar ::= "{" space (ref-definitions-bar-b-kv )? space "}" 1193ref-definitions-bar-b-kv ::= "\"b\"" space ":" space number 1194ref-definitions-foo ::= "{" space (ref-definitions-foo-a-kv )? space "}" 1195ref-definitions-foo-a-kv ::= "\"a\"" space ":" space number 1196root ::= alternative-0 | alternative-1 1197space ::= | " " | "\n"{1,2} [ \t]{0,20} 1198)""" 1199 }); 1200 1201test ({ 1202SUCCESS , 1203"anyOf $ref" , 1204R"""({ 1205"properties": { 1206"a": { 1207"anyOf": [ 1208{"type": "string"}, 1209{"type": "number"} 1210] 1211}, 1212"b": { 1213"anyOf": [ 1214{"$ref": "#/properties/a/anyOf/0"}, 1215{"type": "boolean"} 1216] 1217} 1218}, 1219"type": "object" 1220})""" , 1221R"""( 1222a ::= string | number 1223a-kv ::= "\"a\"" space ":" space a 1224a-rest ::= ( "," space b-kv )? 1225b ::= b-0 | boolean 1226b-0 ::= string 1227b-kv ::= "\"b\"" space ":" space b 1228boolean ::= ("true" | "false") 1229char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1230decimal-part ::= [0-9]{1,16} 1231integral-part ::= [0] | [1-9] [0-9]{0,15} 1232number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1233root ::= "{" space (a-kv a-rest | b-kv )? space "}" 1234space ::= | " " | "\n"{1,2} [ \t]{0,20} 1235string ::= "\"" char* "\"" 1236)""" 1237 }); 1238 1239test ({ 1240SUCCESS , 1241"mix of allOf, anyOf and $ref (similar to https://json.schemastore.org/tsconfig.json)" , 1242R"""({ 1243"allOf": [ 1244{"$ref": "#/definitions/foo"}, 1245{"$ref": "#/definitions/bar"}, 1246{ 1247"anyOf": [ 1248{"$ref": "#/definitions/baz"}, 1249{"$ref": "#/definitions/bam"} 1250] 1251} 1252], 1253"definitions": { 1254"foo": { 1255"properties": {"a": {"type": "number"}} 1256}, 1257"bar": { 1258"properties": {"b": {"type": "number"}} 1259}, 1260"bam": { 1261"properties": {"c": {"type": "number"}} 1262}, 1263"baz": { 1264"properties": {"d": {"type": "number"}} 1265} 1266}, 1267"type": "object" 1268})""" , 1269R"""( 1270a-kv ::= "\"a\"" space ":" space number 1271b-kv ::= "\"b\"" space ":" space number 1272c-kv ::= "\"c\"" space ":" space number 1273d-kv ::= "\"d\"" space ":" space number 1274d-rest ::= ( "," space c-kv )? 1275decimal-part ::= [0-9]{1,16} 1276integral-part ::= [0] | [1-9] [0-9]{0,15} 1277number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1278root ::= "{" space a-kv "," space b-kv ( "," space ( d-kv d-rest | c-kv ) )? space "}" 1279space ::= | " " | "\n"{1,2} [ \t]{0,20} 1280)""" 1281 }); 1282 1283test ({ 1284SUCCESS , 1285"allOf with enum schema" , 1286R"""({ 1287"allOf": [ 1288{"$ref": "#/definitions/foo"} 1289], 1290"definitions": { 1291"foo": { 1292"type": "string", 1293"enum": ["a", "b"] 1294} 1295} 1296})""" , 1297R"""( 1298root ::= ("\"a\"" | "\"b\"") 1299space ::= | " " | "\n"{1,2} [ \t]{0,20} 1300)""" 1301 }); 1302 1303test ({ 1304SUCCESS , 1305"allOf with multiple enum schemas" , 1306R"""({ 1307"allOf": [ 1308{"$ref": "#/definitions/foo"}, 1309{"$ref": "#/definitions/bar"} 1310], 1311"definitions": { 1312"foo": { 1313"type": "string", 1314"enum": ["a", "b", "c"] 1315}, 1316"bar": { 1317"type": "string", 1318"enum": ["b", "c", "d"] 1319} 1320} 1321})""" , 1322R"""( 1323root ::= ("\"b\"" | "\"c\"") 1324space ::= | " " | "\n"{1,2} [ \t]{0,20} 1325)""" 1326 }); 1327 1328test ({ 1329SUCCESS , 1330"conflicting names" , 1331R"""({ 1332"type": "object", 1333"properties": { 1334"number": { 1335"type": "object", 1336"properties": { 1337"number": { 1338"type": "object", 1339"properties": { 1340"root": { 1341"type": "number" 1342} 1343}, 1344"required": [ 1345"root" 1346], 1347"additionalProperties": false 1348} 1349}, 1350"required": [ 1351"number" 1352], 1353"additionalProperties": false 1354} 1355}, 1356"required": [ 1357"number" 1358], 1359"additionalProperties": false, 1360"definitions": {} 1361})""" , 1362R"""( 1363decimal-part ::= [0-9]{1,16} 1364integral-part ::= [0] | [1-9] [0-9]{0,15} 1365number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1366number- ::= "{" space number-number-kv space "}" 1367number-kv ::= "\"number\"" space ":" space number- 1368number-number ::= "{" space number-number-root-kv space "}" 1369number-number-kv ::= "\"number\"" space ":" space number-number 1370number-number-root-kv ::= "\"root\"" space ":" space number 1371root ::= "{" space number-kv space "}" 1372space ::= | " " | "\n"{1,2} [ \t]{0,20} 1373)""" 1374 }); 1375 1376test ({ 1377SUCCESS , 1378"description only (no type) treated as unconstrained" , 1379R"""({"description": "The 0-based index of the last line to be retrieved (inclusive). If None, read until the end of the file."})""" , 1380R"""( 1381array ::= "[" space ( value ("," space value)* )? space "]" 1382boolean ::= ("true" | "false") 1383char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1384decimal-part ::= [0-9]{1,16} 1385integral-part ::= [0] | [1-9] [0-9]{0,15} 1386null ::= "null" 1387number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? 1388object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? space "}" 1389root ::= value 1390space ::= | " " | "\n"{1,2} [ \t]{0,20} 1391string ::= "\"" char* "\"" 1392value ::= object | array | string | number | boolean | null 1393)""" 1394 }); 1395 1396test ({ 1397SUCCESS , 1398"literal string with escapes" , 1399R"""({ 1400"properties": { 1401"code": { 1402"const": " \r \n \" \\ ", 1403"description": "Generated code", 1404"title": "Code", 1405"type": "string" 1406} 1407}, 1408"required": [ 1409"code" 1410], 1411"title": "DecoderResponse", 1412"type": "object" 1413})""" , 1414R"""( 1415code ::= "\" \\r \\n \\\" \\\\ \"" 1416code-kv ::= "\"code\"" space ":" space code 1417root ::= "{" space code-kv space "}" 1418space ::= | " " | "\n"{1,2} [ \t]{0,20} 1419)""" 1420 }); 1421 1422test ({ 1423SUCCESS , 1424"regexp with non-capturing group" , 1425R"""({ 1426"type": "string", 1427"pattern": "^(?:foo|bar)baz$" 1428})""" , 1429R"""( 1430root ::= "\"" (("foo" | "bar") "baz") "\"" 1431space ::= | " " | "\n"{1,2} [ \t]{0,20} 1432)""" 1433 }); 1434 1435test ({ 1436SUCCESS , 1437"regexp with nested non-capturing groups" , 1438R"""({ 1439"type": "string", 1440"pattern": "^(?:(?:ab)+c)?d$" 1441})""" , 1442R"""( 1443root ::= "\"" ((("ab")+ "c")? "d") "\"" 1444space ::= | " " | "\n"{1,2} [ \t]{0,20} 1445)""" 1446 }); 1447 1448test ({ 1449SUCCESS , 1450"unanchored regexp" , 1451R"""({ 1452"type": "string", 1453"pattern": "[0-9]+" 1454})""" , 1455R"""( 1456char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1457root ::= string 1458space ::= | " " | "\n"{1,2} [ \t]{0,20} 1459string ::= "\"" char* "\"" 1460)""" 1461 }); 1462 1463// the rules of the partial conversion (here "root-0") must not leak into the grammar 1464test ({ 1465SUCCESS , 1466"regexp with unsupported shorthand" , 1467R"""({ 1468"type": "string", 1469"pattern": "^[0-9]{3}\\w$" 1470})""" , 1471R"""( 1472char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1473root ::= string 1474space ::= | " " | "\n"{1,2} [ \t]{0,20} 1475string ::= "\"" char* "\"" 1476)""" 1477 }); 1478 1479// a regexp that is invalid under any flavor is still an error 1480test ({ 1481FAILURE , 1482"regexp with unbalanced parentheses" , 1483R"""({ 1484"type": "string", 1485"pattern": "^(a$" 1486})""" , 1487"" 1488 }); 1489 1490// only the property with the bad pattern degrades 1491test ({ 1492SUCCESS , 1493"unsupported regexp in a property" , 1494R"""({ 1495"type": "object", 1496"properties": { 1497"a": { "type": "string", "pattern": "^[a-z\\-]+$" } 1498}, 1499"required": ["a"], 1500"additionalProperties": false 1501})""" , 1502R"""( 1503a ::= string 1504a-kv ::= "\"a\"" space ":" space a 1505char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1506root ::= "{" space a-kv space "}" 1507space ::= | " " | "\n"{1,2} [ \t]{0,20} 1508string ::= "\"" char* "\"" 1509)""" 1510 }); 1511} 1512 1513int main () { 1514test_all ("JSON schema conversion" , [](const TestCase & tc ) { 1515try { 1516tc .verify (json_schema_to_grammar (common_json::parse (tc .schema ), true)); 1517tc .verify_status (SUCCESS ); 1518 }catch (const std::invalid_argument & ex ) { 1519fprintf (stderr ,"Error: %s\n" ,ex .what ()); 1520tc .verify_status (FAILURE ); 1521 } 1522 }); 1523 1524// a document parsed up front gives the same grammar as the JSON, recursion included 1525 { 1526fprintf (stderr ,"- parsed document\n" ); 1527auto schema = common_json::parse (R"""({ 1528"$ref": "#/$defs/node", 1529"$defs": { 1530"node": { 1531"type": "object", 1532"properties": {"next": {"$ref": "#/$defs/node"}, "leaf": {}}, 1533"additionalProperties": false 1534} 1535} 1536})""" ); 1537assert (json_schema_to_grammar (common_chat_schema_from_json (schema ))== json_schema_to_grammar (schema , true)); 1538 } 1539 1540// a property node carries its $ref target, so its grammar names the ref rule 1541 { 1542fprintf (stderr ,"- sub-schema $ref\n" ); 1543auto parameters = common_json::parse (R"""({ 1544"type": "object", 1545"properties": {"item": {"$ref": "#/$defs/item"}}, 1546"$defs": { 1547"item": { 1548"type": "object", 1549"properties": {"a": {"type": "string"}}, 1550"required": ["a"], 1551"additionalProperties": false 1552} 1553} 1554})""" ); 1555TestCase tc { 1556SUCCESS , 1557"sub-schema $ref" , 1558"" , 1559R"""( 1560char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4}) 1561ref-defs-item ::= "{" space ref-defs-item-a-kv space "}" 1562ref-defs-item-a-kv ::= "\"a\"" space ":" space string 1563root ::= ref-defs-item 1564space ::= | " " | "\n"{1,2} [ \t]{0,20} 1565string ::= "\"" char* "\"" 1566)""" , 1567 }; 1568auto doc = common_chat_schema_from_json (parameters ); 1569tc .verify (build_grammar ([& ](const common_grammar_builder & builder ) { 1570const auto & item = static_cast < const common_chat_schema_object &> (* doc .root ).properties .at (0 ); 1571builder .add_schema ("root" ,* item .schema ); 1572 })); 1573 } 1574 1575test_all ("Check the expectations parse" , [](const TestCase & tc ) { 1576if (tc .expected_status == SUCCESS ) { 1577tc .verify_expectation_parseable (); 1578 } 1579 }); 1580}