diff --git a/peglib.h b/peglib.h index a69b183..a9cc9e9 100644 --- a/peglib.h +++ b/peglib.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #if !defined(__cplusplus) || __cplusplus < 201703L @@ -656,8 +657,8 @@ struct ErrorInfo { } void add(const char *token, bool is_literal) { - for (const auto &x : expected_tokens) { - if (x.first == token && x.second == is_literal) { return; } + for (const auto &[t, l] : expected_tokens) { + if (t == token && l == is_literal) { return; } } expected_tokens.push_back(std::make_pair(token, is_literal)); } @@ -938,8 +939,8 @@ public: assert(capture_scope_stack.size() >= 2); auto curr = &capture_scope_stack[capture_scope_stack_size - 1]; auto prev = curr - 1; - for (const auto &kv : *curr) { - (*prev)[kv.first] = kv.second; + for (const auto &[k, v] : *curr) { + (*prev)[k] = v; } } @@ -2001,8 +2002,7 @@ struct HasEmptyElement : public Ope::Visitor { private: void set_error() { is_empty = true; - error_s = refs_.back().first; - error_name = refs_.back().second; + tie(error_s, error_name) = refs_.back(); } std::list> &refs_; }; @@ -2089,6 +2089,7 @@ struct ReferenceChecker : public Ope::Visitor { std::unordered_map error_s; std::unordered_map error_message; + std::unordered_set referenced; private: const Grammar &grammar_; @@ -2943,6 +2944,7 @@ inline void ReferenceChecker::visit(Reference &ope) { error_s[ope.name_] = ope.s_; error_message[ope.name_] = "'" + ope.name_ + "' is not defined."; } else { + if (!referenced.count(ope.name_)) { referenced.insert(ope.name_); } const auto &rule = grammar_.at(ope.name_); if (rule.is_macro) { if (!ope.is_macro_ || ope.args_.size() != rule.params.size()) { @@ -3598,8 +3600,8 @@ private: } // User provided rules - for (const auto &x : rules) { - auto name = x.first; + for (auto [user_name, user_rule] : rules) { + auto name = user_name; auto ignore = false; if (!name.empty() && name[0] == '~') { ignore = true; @@ -3607,7 +3609,7 @@ private: } if (!name.empty()) { auto &rule = grammar[name]; - rule <= x.second; + rule <= user_rule; rule.name = name; rule.ignoreSemanticValue = ignore; } @@ -3616,23 +3618,24 @@ private: // Check duplicated definitions auto ret = data.duplicates.empty(); - for (const auto &x : data.duplicates) { + for (const auto &[name, ptr] : data.duplicates) { if (log) { - const auto &name = x.first; - auto ptr = x.second; auto line = line_info(s, ptr); log(line.first, line.second, "'" + name + "' is already defined."); } } + // Set root definition + auto &start_rule = grammar[data.start]; + // Check if the start rule has ignore operator { - auto &rule = grammar[data.start]; - if (rule.ignoreSemanticValue) { + if (start_rule.ignoreSemanticValue) { if (log) { - auto line = line_info(s, rule.s_); + auto line = line_info(s, start_rule.s_); log(line.first, line.second, - "Ignore operator cannot be applied to '" + rule.name + "'."); + "Ignore operator cannot be applied to '" + start_rule.name + + "'."); } ret = false; } @@ -3641,14 +3644,18 @@ private: if (!ret) { return nullptr; } // Check missing definitions - for (auto &x : grammar) { - auto &rule = x.second; + auto referenced = std::unordered_set{ + WHITESPACE_DEFINITION_NAME, + WORD_DEFINITION_NAME, + RECOVER_DEFINITION_NAME, + start_rule.name, + }; + for (auto &[_, rule] : grammar) { ReferenceChecker vis(grammar, rule.params); rule.accept(vis); - for (const auto &y : vis.error_s) { - const auto &name = y.first; - const auto ptr = y.second; + referenced.insert(vis.referenced.begin(), vis.referenced.end()); + for (const auto &[name, ptr] : vis.error_s) { if (log) { auto line = line_info(s, ptr); log(line.first, line.second, vis.error_message[name]); @@ -3657,6 +3664,17 @@ private: } } + for (auto &[name, rule] : grammar) { + if (!referenced.count(name)) { + if (log) { + auto line = line_info(s, rule.s_); + auto msg = "'" + name + "' is not referenced."; + log(line.first, line.second, msg); + } + ret = false; + } + } + if (!ret) { return nullptr; } // Link references @@ -3669,10 +3687,7 @@ private: // Check left recursion ret = true; - for (auto &x : grammar) { - const auto &name = x.first; - auto &rule = x.second; - + for (auto &[name, rule] : grammar) { DetectLeftRecursion vis(name); rule.accept(vis); if (vis.error_s) { @@ -3686,9 +3701,6 @@ private: if (!ret) { return nullptr; } - // Set root definition - auto &start_rule = grammar[data.start]; - // Check infinite loop for (auto &[name, rule] : grammar) { DetectInfiniteLoop vis(rule.s_, name); @@ -3721,9 +3733,7 @@ private: } // Apply instructions - for (const auto &item : data.instructions) { - const auto &name = item.first; - const auto &instruction = item.second; + for (const auto &[name, instruction] : data.instructions) { auto &rule = grammar[name]; if (instruction.type == "precedence") { diff --git a/test/test2.cc b/test/test2.cc index e93a48c..875f739 100644 --- a/test/test2.cc +++ b/test/test2.cc @@ -1036,12 +1036,23 @@ TEST_CASE("Macro passes an arg to another macro", "[macro]") { A <- B(C) B(D) <- D C <- 'c' - D <- 'd' )"); REQUIRE(parser.parse("c")); } +TEST_CASE("Unreferenced rule", "[macro]") { + parser parser(R"( + A <- B(C) + B(D) <- D + C <- 'c' + D <- 'd' + )"); + + bool ret = parser; + REQUIRE(ret == false); +} + TEST_CASE("Nested macro call", "[macro]") { parser parser(R"( A <- B(T)