CoolFace
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 3d agoView on Hugging Face
0likes1.1kdownloads
test-gbnf-generation.cpp518 linesDownload Raw Back to peg-parser
1#include "tests.h"2 3#include "json-schema-to-grammar.h"4 5#include <regex>6 7static std::string trim_leading_space(const std::string & s) {8    static const std::regex leading_ws_re = std::regex(R"((^|\n)\s+)");9    return std::regex_replace(s, leading_ws_re, "$1");10}11 12static void assert_gbnf_equal(testing & t, const std::string & expected, const std::string & actual) {13    t.assert_equal("gbnf are equal", trim_leading_space(expected), trim_leading_space(actual));14}15 16void test_gbnf_generation(testing &t) {17    t.test("literal grammar generation", [](testing &t) {18        auto parser = build_peg_parser([](common_peg_parser_builder & p) {19            return p.literal("hello");20        });21 22        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {23            parser.build_grammar(builder);24        });25 26        assert_gbnf_equal(t, R"""(27            root ::= "hello"28            space ::= | " " | "\n"{1,2} [ \t]{0,20}29        )""", gbnf);30    });31 32    t.test("char class grammar", [](testing &t) {33        auto parser = build_peg_parser([](common_peg_parser_builder & p) {34            return p.chars("[a-z]", 1, 1);35        });36 37        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {38            parser.build_grammar(builder);39        });40 41        assert_gbnf_equal(t, R"""(42            root ::= [a-z]43            space ::= | " " | "\n"{1,2} [ \t]{0,20}44        )""", gbnf);45    });46 47    t.test("sequence grammar", [](testing &t) {48        auto parser = build_peg_parser([](common_peg_parser_builder & p) {49            return p.literal("hello") + p.literal(" ") + p.literal("world");50        });51 52        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {53            parser.build_grammar(builder);54        });55 56        assert_gbnf_equal(t, R"""(57            root ::= "hello" " " "world"58            space ::= | " " | "\n"{1,2} [ \t]{0,20}59        )""", gbnf);60    });61 62    t.test("choice grammar", [](testing &t) {63        auto parser = build_peg_parser([](common_peg_parser_builder & p) {64            return p.literal("cat") | p.literal("dog");65        });66 67        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {68            parser.build_grammar(builder);69        });70 71        assert_gbnf_equal(t, R"""(72            root ::= "cat" | "dog"73            space ::= | " " | "\n"{1,2} [ \t]{0,20}74        )""", gbnf);75    });76 77    t.test("one_or_more grammar", [](testing &t) {78        auto parser = build_peg_parser([](common_peg_parser_builder & p) {79            return p.one_or_more(p.literal("a"));80        });81 82        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {83            parser.build_grammar(builder);84        });85 86        assert_gbnf_equal(t, R"""(87            root ::= "a"+88            space ::= | " " | "\n"{1,2} [ \t]{0,20}89        )""", gbnf);90    });91 92    t.test("zero_or_more grammar", [](testing &t) {93        auto parser = build_peg_parser([](common_peg_parser_builder & p) {94            return p.zero_or_more(p.literal("a"));95        });96 97        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {98            parser.build_grammar(builder);99        });100 101        assert_gbnf_equal(t, R"""(102            root ::= "a"*103            space ::= | " " | "\n"{1,2} [ \t]{0,20}104        )""", gbnf);105    });106 107    t.test("optional grammar", [](testing &t) {108        auto parser = build_peg_parser([](common_peg_parser_builder & p) {109            return p.literal("hello") + p.optional(p.literal(" world"));110        });111 112        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {113            parser.build_grammar(builder);114        });115 116        assert_gbnf_equal(t, R"""(117            root ::= "hello" " world"?118            space ::= | " " | "\n"{1,2} [ \t]{0,20}119        )""", gbnf);120    });121 122    t.test("until grammar", [](testing &t) {123        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {124            return p.until("</tag>");125        });126 127        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {128            parser.build_grammar(builder);129        });130 131        assert_gbnf_equal(t, R"""(132            root ::= until-0133            space ::= | " " | "\n"{1,2} [ \t]{0,20}134            until-0 ::= | [<] until-0-01 | [^<] until-0135            until-0-01 ::= | [<] until-0-01 | [/] until-0-02 | [^/<] until-0136            until-0-02 ::= | [<] until-0-01 | [t] until-0-03 | [^<t] until-0137            until-0-03 ::= | [<] until-0-01 | [a] until-0-04 | [^<a] until-0138            until-0-04 ::= | [<] until-0-01 | [g] until-0-05 | [^<g] until-0139            until-0-05 ::= | [<] until-0-01 | [^<>] until-0140        )""", gbnf);141    });142 143    t.test("until grammar overlapping delimiter", [](testing &t) {144        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {145            return p.until("\n</parameter>\n");146        });147 148        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {149            parser.build_grammar(builder);150        });151 152        assert_gbnf_equal(t, R"""(153            root ::= until-0154            space ::= | " " | "\n"{1,2} [ \t]{0,20}155            until-0 ::= | [\n] until-0-01 | [^\n] until-0156            until-0-01 ::= | [\n] until-0-01 | [<] until-0-02 | [^\n<] until-0157            until-0-02 ::= | [\n] until-0-01 | [/] until-0-03 | [^\n/] until-0158            until-0-03 ::= | [\n] until-0-01 | [p] until-0-04 | [^\np] until-0159            until-0-04 ::= | [\n] until-0-01 | [a] until-0-05 | [^\na] until-0160            until-0-05 ::= | [\n] until-0-01 | [r] until-0-06 | [^\nr] until-0161            until-0-06 ::= | [\n] until-0-01 | [a] until-0-07 | [^\na] until-0162            until-0-07 ::= | [\n] until-0-01 | [m] until-0-08 | [^\nm] until-0163            until-0-08 ::= | [\n] until-0-01 | [e] until-0-09 | [^\ne] until-0164            until-0-09 ::= | [\n] until-0-01 | [t] until-0-10 | [^\nt] until-0165            until-0-10 ::= | [\n] until-0-01 | [e] until-0-11 | [^\ne] until-0166            until-0-11 ::= | [\n] until-0-01 | [r] until-0-12 | [^\nr] until-0167            until-0-12 ::= | [\n] until-0-01 | [>] until-0-13 | [^\n>] until-0168            until-0-13 ::= | [^\n] until-0169        )""", gbnf);170    });171 172    // DeepSeek-V3.2 tag prefix. The DSML token (|DSML|) embeds U+FF5C,173    // so the delimiter mixes ASCII and multi-byte codepoints.174    t.test("until grammar unicode delimiter", [](testing &t) {175        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {176            return p.until("<|DSML|");177        });178 179        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {180            parser.build_grammar(builder);181        });182 183        assert_gbnf_equal(t, R"""(184            root ::= until-0185            space ::= | " " | "\n"{1,2} [ \t]{0,20}186            until-0 ::= | [<] until-0-01 | [^<] until-0187            until-0-01 ::= | [<] until-0-01 | [\uFF5C] until-0-02 | [^<\uFF5C] until-0188            until-0-02 ::= | [<] until-0-01 | [D] until-0-03 | [^<D] until-0189            until-0-03 ::= | [<] until-0-01 | [S] until-0-04 | [^<S] until-0190            until-0-04 ::= | [<] until-0-01 | [M] until-0-05 | [^<M] until-0191            until-0-05 ::= | [<] until-0-01 | [L] until-0-06 | [^<L] until-0192            until-0-06 ::= | [<] until-0-01 | [^<\uFF5C] until-0193        )""", gbnf);194    });195 196    t.test("until grammar multiple delimiters", [](testing &t) {197        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {198            return p.until_one_of({"ab", "cd", "ef"});199        });200 201        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {202            parser.build_grammar(builder);203        });204 205        assert_gbnf_equal(t, R"""(206            root ::= until-0207            space ::= | " " | "\n"{1,2} [ \t]{0,20}208            until-0 ::= | [a] until-0-01 | [c] until-0-03 | [e] until-0-05 | [^ace] until-0209            until-0-01 ::= | [a] until-0-01 | [c] until-0-03 | [e] until-0-05 | [^abce] until-0210            until-0-03 ::= | [a] until-0-01 | [c] until-0-03 | [e] until-0-05 | [^acde] until-0211            until-0-05 ::= | [a] until-0-01 | [c] until-0-03 | [e] until-0-05 | [^acef] until-0212        )""", gbnf);213    });214 215    t.test("ac grammar", [](testing &t) {216        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {217            return p.ac(p.until("</tag>") + p.literal("</tag>"), "</tag>");218        });219 220        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {221            parser.build_grammar(builder);222        });223 224        assert_gbnf_equal(t, R"""(225            ac-3 ::= [<] ac-3-01 | [^<] ac-3226            ac-3-01 ::= [<] ac-3-01 | [/] ac-3-02 | [^/<] ac-3227            ac-3-02 ::= [<] ac-3-01 | [t] ac-3-03 | [^<t] ac-3228            ac-3-03 ::= [<] ac-3-01 | [a] ac-3-04 | [^<a] ac-3229            ac-3-04 ::= [<] ac-3-01 | [g] ac-3-05 | [^<g] ac-3230            ac-3-05 ::= [>] | [<] ac-3-01 | [^<>] ac-3231            root ::= ac-3232            space ::= | " " | "\n"{1,2} [ \t]{0,20}233        )""", gbnf);234    });235 236    t.test("ac grammar terminates at first delimiter", [](testing &t) {237        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {238            return p.ac(p.until("\n</parameter>\n") + p.literal("\n</parameter>\n"), "\n</parameter>\n");239        });240 241        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {242            parser.build_grammar(builder);243        });244 245        assert_gbnf_equal(t, R"""(246            ac-3 ::= [\n] ac-3-01 | [^\n] ac-3247            ac-3-01 ::= [\n] ac-3-01 | [<] ac-3-02 | [^\n<] ac-3248            ac-3-02 ::= [\n] ac-3-01 | [/] ac-3-03 | [^\n/] ac-3249            ac-3-03 ::= [\n] ac-3-01 | [p] ac-3-04 | [^\np] ac-3250            ac-3-04 ::= [\n] ac-3-01 | [a] ac-3-05 | [^\na] ac-3251            ac-3-05 ::= [\n] ac-3-01 | [r] ac-3-06 | [^\nr] ac-3252            ac-3-06 ::= [\n] ac-3-01 | [a] ac-3-07 | [^\na] ac-3253            ac-3-07 ::= [\n] ac-3-01 | [m] ac-3-08 | [^\nm] ac-3254            ac-3-08 ::= [\n] ac-3-01 | [e] ac-3-09 | [^\ne] ac-3255            ac-3-09 ::= [\n] ac-3-01 | [t] ac-3-10 | [^\nt] ac-3256            ac-3-10 ::= [\n] ac-3-01 | [e] ac-3-11 | [^\ne] ac-3257            ac-3-11 ::= [\n] ac-3-01 | [r] ac-3-12 | [^\nr] ac-3258            ac-3-12 ::= [\n] ac-3-01 | [>] ac-3-13 | [^\n>] ac-3259            ac-3-13 ::= [\n] | [^\n] ac-3260            root ::= ac-3261            space ::= | " " | "\n"{1,2} [ \t]{0,20}262        )""", gbnf);263    });264 265    t.test("ac grammar multiple delimiters", [](testing &t) {266        auto parser = build_peg_parser([](common_peg_parser_builder & p)  {267            return p.ac(p.eps(), std::vector<std::string>{"ab", "cd", "ef"});268        });269 270        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {271            parser.build_grammar(builder);272        });273 274        assert_gbnf_equal(t, R"""(275            ac-1 ::= [a] ac-1-01 | [c] ac-1-03 | [e] ac-1-05 | [^ace] ac-1276            ac-1-01 ::= [b] | [a] ac-1-01 | [c] ac-1-03 | [e] ac-1-05 | [^abce] ac-1277            ac-1-03 ::= [d] | [a] ac-1-01 | [c] ac-1-03 | [e] ac-1-05 | [^acde] ac-1278            ac-1-05 ::= [f] | [a] ac-1-01 | [c] ac-1-03 | [e] ac-1-05 | [^acef] ac-1279            root ::= ac-1280            space ::= | " " | "\n"{1,2} [ \t]{0,20}281        )""", gbnf);282    });283 284    t.test("complex expressions with parentheses", [](testing &t) {285        auto parser = build_peg_parser([](common_peg_parser_builder & p) {286            return p.one_or_more(p.literal("a") | p.literal("b"));287        });288 289        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {290            parser.build_grammar(builder);291        });292 293        assert_gbnf_equal(t, R"""(294            root ::= ("a" | "b")+295            space ::= | " " | "\n"{1,2} [ \t]{0,20}296        )""", gbnf);297    });298 299    t.test("rule references", [](testing &t) {300        auto parser = build_peg_parser([](common_peg_parser_builder & p) {301            auto digit = p.rule("digit", p.chars("[0-9]", 1, 1));302            return p.one_or_more(digit);303        });304 305        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {306            parser.build_grammar(builder);307        });308 309        assert_gbnf_equal(t, R"""(310            digit ::= [0-9]311            root ::= digit+312            space ::= | " " | "\n"{1,2} [ \t]{0,20}313        )""", gbnf);314    });315 316    t.test("escaping in literals", [](testing &t) {317        auto parser = build_peg_parser([](common_peg_parser_builder & p) {318            return p.literal("hello\nworld\n!");319        });320 321        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {322            parser.build_grammar(builder);323        });324 325        assert_gbnf_equal(t, R"""(326            root ::= "hello\nworld\n!"327            space ::= | " " | "\n"{1,2} [ \t]{0,20}328        )""", gbnf);329    });330 331    t.test("operator<< (whitespace insertion)", [](testing &t) {332        auto parser = build_peg_parser([](common_peg_parser_builder & p) {333            return p.literal("hello") << p.literal("world");334        });335 336        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {337            parser.build_grammar(builder);338        });339 340        assert_gbnf_equal(t, R"""(341            root ::= "hello" space "world"342            space ::= | " " | "\n"{1,2} [ \t]{0,20}343        )""", gbnf);344    });345 346    t.test("emit only reachable rules", [](testing &t) {347        auto parser = build_peg_parser([](common_peg_parser_builder & p) {348            p.rule("orphan", p.literal("orphan"));349            return p.literal("hello") + p.rule("child", p.literal(" world"));350        });351 352        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {353            parser.build_grammar(builder);354        });355 356        assert_gbnf_equal(t, R"""(357            child ::= " world"358            root ::= "hello" child359            space ::= | " " | "\n"{1,2} [ \t]{0,20}360        )""", gbnf);361    });362 363    t.test("tagged choice inside sequence gets parenthesized", [](testing &t) {364        auto parser = build_peg_parser([](common_peg_parser_builder & p) {365            return p.literal("a") + p.tag("t", p.literal("b") | p.literal("c"));366        });367 368        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {369            parser.build_grammar(builder);370        });371 372        assert_gbnf_equal(t, R"""(373            root ::= "a" ("b" | "c")374            space ::= | " " | "\n"{1,2} [ \t]{0,20}375        )""", gbnf);376    });377 378    t.test("tagged sequence inside choice gets parenthesized", [](testing &t) {379        auto parser = build_peg_parser([](common_peg_parser_builder & p) {380            return p.tag("t", p.literal("a") + p.literal("b")) | p.literal("c");381        });382 383        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {384            parser.build_grammar(builder);385        });386 387        assert_gbnf_equal(t, R"""(388            root ::= "a" "b" | "c"389            space ::= | " " | "\n"{1,2} [ \t]{0,20}390        )""", gbnf);391    });392 393    t.test("atomic choice inside repetition gets parenthesized", [](testing &t) {394        auto parser = build_peg_parser([](common_peg_parser_builder & p) {395            return p.one_or_more(p.atomic(p.literal("a") | p.literal("b")));396        });397 398        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {399            parser.build_grammar(builder);400        });401 402        assert_gbnf_equal(t, R"""(403            root ::= ("a" | "b")+404            space ::= | " " | "\n"{1,2} [ \t]{0,20}405        )""", gbnf);406    });407 408    t.test("silent parser emits nothing in gbnf", [](testing &t) {409        auto parser = build_peg_parser([](common_peg_parser_builder & p) {410            return p.literal("hello") + p.gbnf(p.literal("world"), "");411        });412 413        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {414            parser.build_grammar(builder);415        });416 417        assert_gbnf_equal(t, R"""(418            root ::= "hello"419            space ::= | " " | "\n"{1,2} [ \t]{0,20}420        )""", gbnf);421    });422 423    t.test("silent choice inside sequence emits nothing", [](testing &t) {424        auto parser = build_peg_parser([](common_peg_parser_builder & p) {425            return p.literal("a") + p.gbnf(p.literal("b") | p.literal("c"), "") + p.literal("d");426        });427 428        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {429            parser.build_grammar(builder);430        });431 432        assert_gbnf_equal(t, R"""(433            root ::= "a" "d"434            space ::= | " " | "\n"{1,2} [ \t]{0,20}435        )""", gbnf);436    });437 438    t.test("silent wrapped in tag emits nothing", [](testing &t) {439        auto parser = build_peg_parser([](common_peg_parser_builder & p) {440            return p.literal("a") + p.tag("t", p.gbnf(p.literal("b"), ""));441        });442 443        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {444            parser.build_grammar(builder);445        });446 447        assert_gbnf_equal(t, R"""(448            root ::= "a"449            space ::= | " " | "\n"{1,2} [ \t]{0,20}450        )""", gbnf);451    });452 453    t.test("gbnf parser emits custom grammar", [](testing &t) {454        auto parser = build_peg_parser([](common_peg_parser_builder & p) {455            return p.literal("a") + p.gbnf(p.literal("b"), "[a-z]+");456        });457 458        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {459            parser.build_grammar(builder);460        });461 462        assert_gbnf_equal(t, R"""(463            root ::= "a" [a-z]+464            space ::= | " " | "\n"{1,2} [ \t]{0,20}465        )""", gbnf);466    });467 468    t.test("nested transparent wrappers get parenthesized", [](testing &t) {469        auto parser = build_peg_parser([](common_peg_parser_builder & p) {470            return p.literal("x") + p.tag("outer", p.atomic(p.literal("a") | p.literal("b")));471        });472 473        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {474            parser.build_grammar(builder);475        });476 477        assert_gbnf_equal(t, R"""(478            root ::= "x" ("a" | "b")479            space ::= | " " | "\n"{1,2} [ \t]{0,20}480        )""", gbnf);481    });482 483    t.test("emit only trigger rules (and references)", [](testing &t) {484        auto parser = build_peg_parser([](common_peg_parser_builder & p) {485            auto rule1 = p.rule("rule-1", p.literal("a") + p.ref("rule-2"));486            p.rule("rule-2", p.literal("b") + p.ref("rule-3"), true);487            p.rule("rule-3", p.literal("c") + p.ref("rule-4"));488            p.rule("rule-4", p.literal("d"), true);489            return rule1;490        });491 492        auto gbnf = build_grammar([&](const common_grammar_builder & builder) {493            parser.build_grammar(builder);494        });495 496        assert_gbnf_equal(t, R"""(497            root ::= rule-1498            rule-1 ::= "a" rule-2499            rule-2 ::= "b" rule-3500            rule-3 ::= "c" rule-4501            rule-4 ::= "d"502            space ::= | " " | "\n"{1,2} [ \t]{0,20}503        )""", gbnf);504 505        auto gbnf_lazy = build_grammar([&](const common_grammar_builder & builder) {506            parser.build_grammar(builder, true);507        });508 509        assert_gbnf_equal(t, R"""(510            root ::= rule-2 | rule-4511            rule-2 ::= "b" rule-3512            rule-3 ::= "c" rule-4513            rule-4 ::= "d"514            space ::= | " " | "\n"{1,2} [ \t]{0,20}515        )""", gbnf_lazy);516    });517}518