GBNF generation implementation
| 1597 | |
| 1598 | // GBNF generation implementation |
| 1599 | void common_peg_arena::build_grammar(const common_grammar_builder & builder, bool lazy) const { |
| 1600 | auto schema_delegates = [](const common_peg_schema_parser & s) -> bool { |
| 1601 | if (!s.schema) { |
| 1602 | return true; |
| 1603 | } |
| 1604 | if (s.raw && s.schema->contains("type")) { |
| 1605 | const auto & type_val = s.schema->at("type"); |
| 1606 | if (type_val.is_string() && type_val == "string") { |
| 1607 | return true; |
| 1608 | } |
| 1609 | // Handle nullable types like ["string", "null"] - delegate when the |
| 1610 | // non-null type is string, since the tagged format uses raw text |
| 1611 | if (type_val.is_array()) { |
| 1612 | for (const auto & t : type_val) { |
| 1613 | if (t.is_string() && t.get<std::string>() != "null") { |
| 1614 | return t.get<std::string>() == "string"; |
| 1615 | } |
| 1616 | } |
| 1617 | } |
| 1618 | } |
| 1619 | // Delegate for enum schemas in raw mode - enum values are literal strings |
| 1620 | if (s.raw && !s.schema->contains("type") && s.schema->contains("enum")) { |
| 1621 | return true; |
| 1622 | } |
| 1623 | return false; |
| 1624 | }; |
| 1625 | |
| 1626 | // Unwrap the parser so we can properly check if it's a sequence or choice |
| 1627 | auto effective_parser = [&](common_peg_parser_id id) -> const common_peg_parser_variant & { |
| 1628 | while (true) { |
| 1629 | const auto & p = parsers_.at(id); |
| 1630 | if (const auto * tag = std::get_if<common_peg_tag_parser>(&p)) { |
| 1631 | id = tag->child; |
| 1632 | } else if (const auto * atomic = std::get_if<common_peg_atomic_parser>(&p)) { |
| 1633 | id = atomic->child; |
| 1634 | } else if (const auto * schema = std::get_if<common_peg_schema_parser>(&p)) { |
| 1635 | if (schema_delegates(*schema)) { |
| 1636 | id = schema->child; |
| 1637 | } else { |
| 1638 | return p; |
| 1639 | } |
| 1640 | } else { |
| 1641 | return p; |
| 1642 | } |
| 1643 | } |
| 1644 | }; |
| 1645 | |
| 1646 | // Generate GBNF for a parser |
| 1647 | std::function<std::string(common_peg_parser_id)> to_gbnf = [&](common_peg_parser_id id) -> std::string { |
| 1648 | const auto & parser = parsers_.at(id); |
| 1649 | |
| 1650 | return std::visit([&](const auto & p) -> std::string { |
| 1651 | using T = std::decay_t<decltype(p)>; |
| 1652 | |
| 1653 | if constexpr (std::is_same_v<T, common_peg_epsilon_parser> || |
| 1654 | std::is_same_v<T, common_peg_start_parser> || |
| 1655 | std::is_same_v<T, common_peg_end_parser>) { |
| 1656 | return ""; |