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