diff --git a/README.md b/README.md index 98f8ffd..577eaf5 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,28 @@ peglib::peg parser( ); ``` +*Semantic predicate* support is available. We can do it by throwing a `peglib::parse_error` exception in a semantic action. + +```c++ +peglib::peg parser("NUMBER <- [0-9]+"); + +parser["NUMBER"] = [](const char* s, size_t n) { + auto val = stol(string(s, n), nullptr, 10); + if (val != 100) { + throw peglib::parse_error("value error!!"); + } + return val; +}; + +long val; +auto ret = parser.parse("100", val); +assert(ret == true); +assert(val == 100); + +ret = parser.parse("200", val); +assert(ret == false); +``` + Simple interface ---------------- diff --git a/peglib.h b/peglib.h index a634209..1b210f8 100644 --- a/peglib.h +++ b/peglib.h @@ -280,13 +280,22 @@ any call(F fn, Args&&... args) { return any(fn(std::forward(args)...)); } -#if 0 /* - * Predicate + * Semantic predicate */ -typedef std::function Predicate; -#endif +typedef std::function SemanticPredicate; +struct parse_error { + parse_error() = default; + parse_error(const char* s) : s_(s) {} + const char* what() const { return s_.empty() ? nullptr : s_.c_str(); } +private: + std::string s_; +}; + +/* + * Action + */ class Action { public: @@ -1212,9 +1221,6 @@ public: std::string name; size_t id; -#if 0 - Predicate predicate; -#endif std::vector actions; std::function error_message; bool ignoreSemanticValue; @@ -1262,7 +1268,7 @@ inline size_t Holder::parse(const char* s, size_t n, SemanticValues& sv, Context anchorn = len; // Invoke action - if (success(len) && !outer_->ignoreSemanticValue) { + if (success(len)) { assert(!outer_->actions.empty()); auto i = chldsv.choice + 1; // Index 0 is for the default action @@ -1278,16 +1284,17 @@ inline size_t Holder::parse(const char* s, size_t n, SemanticValues& sv, Context chldsv.n = len; } - val = reduce(chldsv, dt, action); + try { + val = reduce(chldsv, dt, action); + } catch (const parse_error& e) { + if (e.what()) { + c.message_pos = s; + c.message = e.what(); + } + len = -1; + } } -#if 0 - // Predicate check - if (success(len) && outer_->predicate && !outer_->predicate(anchors, anchorn, val, dt)) { - len = -1; - } -#endif - c.pop(); }); diff --git a/test/test.cc b/test/test.cc index 6374af5..31a1cfd 100644 --- a/test/test.cc +++ b/test/test.cc @@ -484,29 +484,6 @@ TEST_CASE("Calculator test with AST", "[general]") REQUIRE(val == -3); } -#if 0 -TEST_CASE("Predicate test", "[general]") -{ - peg parser("NUMBER <- [0-9]+"); - - parser["NUMBER"] = [](const char* s, size_t n) { - return stol(string(s, n), nullptr, 10); - }; - - parser["NUMBER"].predicate = [](const char* s, size_t n, const any& val, const any& dt) { - return val.get() == 100; - }; - - long val; - auto ret = parser.parse("100", val); - REQUIRE(ret == true); - REQUIRE(val == 100); - - ret = parser.parse("200", val); - REQUIRE(ret == false); -} -#endif - TEST_CASE("Ignore semantic value test", "[general]") { peg parser( @@ -639,6 +616,28 @@ TEST_CASE("User rule test", "[user rule]") REQUIRE(g.parse(" Hello BNF! ") == true); } + +TEST_CASE("Semantic predicate test", "[predicate]") +{ + peg parser("NUMBER <- [0-9]+"); + + parser["NUMBER"] = [](const char* s, size_t n) { + auto val = stol(string(s, n), nullptr, 10); + if (val != 100) { + throw parse_error("value error!!"); + } + return val; + }; + + long val; + auto ret = parser.parse("100", val); + REQUIRE(ret == true); + REQUIRE(val == 100); + + ret = parser.parse("200", val); + REQUIRE(ret == false); +} + bool exact(Grammar& g, const char* d, const char* s) { auto n = strlen(s); auto r = g[d].parse(s, n);