| 1537 | } |
| 1538 | |
| 1539 | static std::unordered_set<std::string> collect_reachable_rules( |
| 1540 | const common_peg_arena & arena, |
| 1541 | const common_peg_parser_id & rule |
| 1542 | ) { |
| 1543 | std::unordered_set<std::string> reachable; |
| 1544 | std::unordered_set<std::string> visited; |
| 1545 | |
| 1546 | std::function<void(common_peg_parser_id)> visit = [&](common_peg_parser_id id) { |
| 1547 | const auto & parser = arena.get(id); |
| 1548 | |
| 1549 | std::visit([&](const auto & p) { |
| 1550 | using T = std::decay_t<decltype(p)>; |
| 1551 | |
| 1552 | if constexpr (std::is_same_v<T, common_peg_epsilon_parser> || |
| 1553 | std::is_same_v<T, common_peg_start_parser> || |
| 1554 | std::is_same_v<T, common_peg_end_parser> || |
| 1555 | std::is_same_v<T, common_peg_until_parser> || |
| 1556 | std::is_same_v<T, common_peg_literal_parser> || |
| 1557 | std::is_same_v<T, common_peg_chars_parser> || |
| 1558 | std::is_same_v<T, common_peg_space_parser> || |
| 1559 | std::is_same_v<T, common_peg_any_parser> || |
| 1560 | std::is_same_v<T, common_peg_string_parser>) { |
| 1561 | // These parsers do not have any children |
| 1562 | } else if constexpr (std::is_same_v<T, common_peg_sequence_parser>) { |
| 1563 | for (auto child : p.children) { |
| 1564 | visit(child); |
| 1565 | } |
| 1566 | } else if constexpr (std::is_same_v<T, common_peg_choice_parser>) { |
| 1567 | for (auto child : p.children) { |
| 1568 | visit(child); |
| 1569 | } |
| 1570 | } else if constexpr (std::is_same_v<T, common_peg_repetition_parser> || |
| 1571 | std::is_same_v<T, common_peg_and_parser> || |
| 1572 | std::is_same_v<T, common_peg_not_parser> || |
| 1573 | std::is_same_v<T, common_peg_tag_parser> || |
| 1574 | std::is_same_v<T, common_peg_atomic_parser> || |
| 1575 | std::is_same_v<T, common_peg_gbnf_parser> || |
| 1576 | std::is_same_v<T, common_peg_schema_parser>) { |
| 1577 | visit(p.child); |
| 1578 | } else if constexpr (std::is_same_v<T, common_peg_rule_parser>) { |
| 1579 | if (visited.find(p.name) == visited.end()) { |
| 1580 | visited.insert(p.name); |
| 1581 | reachable.insert(p.name); |
| 1582 | visit(p.child); |
| 1583 | } |
| 1584 | } else if constexpr (std::is_same_v<T, common_peg_ref_parser>) { |
| 1585 | // Traverse rules so we pick up everything |
| 1586 | auto referenced_rule = arena.get_rule(p.name); |
| 1587 | visit(referenced_rule); |
| 1588 | } else { |
| 1589 | static_assert(is_always_false_v<T>); |
| 1590 | } |
| 1591 | }, parser); |
| 1592 | }; |
| 1593 | |
| 1594 | visit(rule); |
| 1595 | return reachable; |
| 1596 | } |