Replace peg::any with std::any (when C++17 is available)

This commit is contained in:
Francesco Guastella 2019-11-21 17:52:31 +01:00
parent 523137c5dd
commit 1afa4988bc
7 changed files with 189 additions and 183 deletions

View File

@ -1,37 +1,26 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 3.1.0)
project("cpp-peglib")
# Check if a supported compiler is used and add c++11 flag: # Check if a supported compiler is used to setup the C++ standard to use:
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") get_property(known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) list(FIND known_features "cxx_std_17" found)
message(FATAL_ERROR "Need at least gcc 4.9 to compile.") if(NOT ${found} EQUAL -1)
endif() # C++17 standard is supported
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_CXX_STANDARD 17)
elseif(MSVC) else()
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19) # Check for C++11 standard support
message(FATAL_ERROR "Visual Studio 2015 or newer is required.") list(FIND known_features "cxx_std_11" found)
endif() if(NOT ${found} EQUAL -1)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # C++11 standard is supported
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) set(CMAKE_CXX_STANDARD 11)
message(FATAL_ERROR "Need at least AppleClang 7.0 to compile.")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4)
message(FATAL_ERROR "Clang below version 3.4 will most likely not work. Please upgrade your compiler.")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else() # no GNU, no MSVC, no Clang
message(WARNING "You are using an unsupported compiler. Compilation has only been tested with MSVC, GCC and Clang.")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-std=c++11 HAS_CXX11_FLAG)
if(HAS_CXX11_FLAG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
message(FATAL_ERROR "Your compiler doesn't support the '-std=c++11' flag.")
endif() endif()
endif() endif()
if(${found} EQUAL -1)
message(FATAL_ERROR "Your compiler is not supported.")
endif()
set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")

View File

@ -110,7 +110,14 @@ There are four semantic actions available:
`any& dt` is a 'read-write' context data which can be used for whatever purposes. The initial context data is set in `peg::parser::parse` method. `any& dt` is a 'read-write' context data which can be used for whatever purposes. The initial context data is set in `peg::parser::parse` method.
`peg::any` is a simpler implementatin of [boost::any](http://www.boost.org/doc/libs/1_57_0/doc/html/any.html). It can wrap arbitrary data type. `peg::any` is a simpler implementatin of [boost::any](http://www.boost.org/doc/libs/1_57_0/doc/html/any.html). It can wrap arbitrary data type.
If the compiler in use supports C++17, by default `peg::any` is defined as an alias to `std::any`.
To force using the simpler `any` implementation that comes with `cpp-peglib`, define `PEGLIB_USE_STD_ANY` as 0 before including `peglib.h`:
```cpp
#define PEGLIB_USE_STD_ANY 0
#include <peglib.h>
[...]
```
A semantic action can return a value of arbitrary data type, which will be wrapped by `peg::any`. If a user returns nothing in a semantic action, the first semantic value in the `const SemanticValues& sv` argument will be returned. (Yacc parser has the same behavior.) A semantic action can return a value of arbitrary data type, which will be wrapped by `peg::any`. If a user returns nothing in a semantic action, the first semantic value in the `const SemanticValues& sv` argument will be returned. (Yacc parser has the same behavior.)

View File

@ -10,20 +10,19 @@
#include <cstdlib> #include <cstdlib>
using namespace peg; using namespace peg;
using namespace std;
int main(int argc, const char** argv) int main(int argc, const char** argv)
{ {
if (argc < 2 || string("--help") == argv[1]) { if (argc < 2 || std::string("--help") == argv[1]) {
cout << "usage: calc [formula]" << endl; std::cout << "usage: calc [formula]" << std::endl;
return 1; return 1;
} }
auto reduce = [](const SemanticValues& sv) -> long { auto reduce = [](const SemanticValues& sv) -> long {
auto result = sv[0].get<long>(); auto result = any_cast<long>(sv[0]);
for (auto i = 1u; i < sv.size(); i += 2) { for (auto i = 1u; i < sv.size(); i += 2) {
auto num = sv[i + 1].get<long>(); auto num = any_cast<long>(sv[i + 1]);
auto ope = sv[i].get<char>(); auto ope = any_cast<char>(sv[i]);
switch (ope) { switch (ope) {
case '+': result += num; break; case '+': result += num; break;
case '-': result -= num; break; case '-': result -= num; break;
@ -53,11 +52,11 @@ int main(int argc, const char** argv)
auto expr = argv[1]; auto expr = argv[1];
long val = 0; long val = 0;
if (parser.parse(expr, val)) { if (parser.parse(expr, val)) {
cout << expr << " = " << val << endl; std::cout << expr << " = " << val << std::endl;
return 0; return 0;
} }
cout << "syntax error..." << endl; std::cout << "syntax error..." << std::endl;
return -1; return -1;
} }

View File

@ -10,7 +10,6 @@
#include <cstdlib> #include <cstdlib>
using namespace peg; using namespace peg;
using namespace std;
// //
// PEG syntax: // PEG syntax:
@ -24,16 +23,16 @@ using namespace std;
// //
int main(int argc, const char** argv) int main(int argc, const char** argv)
{ {
if (argc < 2 || string("--help") == argv[1]) { if (argc < 2 || std::string("--help") == argv[1]) {
cout << "usage: calc [formula]" << endl; std::cout << "usage: calc [formula]" << std::endl;
return 1; return 1;
} }
auto reduce = [](const SemanticValues& sv) -> long { auto reduce = [](const SemanticValues& sv) -> long {
auto result = sv[0].get<long>(); auto result = any_cast<long>(sv[0]);
for (auto i = 1u; i < sv.size(); i += 2) { for (auto i = 1u; i < sv.size(); i += 2) {
auto num = sv[i + 1].get<long>(); auto num = any_cast<long>(sv[i + 1]);
auto ope = sv[i].get<char>(); auto ope = any_cast<char>(sv[i]);
switch (ope) { switch (ope) {
case '+': result += num; break; case '+': result += num; break;
case '-': result -= num; break; case '-': result -= num; break;
@ -56,7 +55,7 @@ int main(int argc, const char** argv)
auto expr = argv[1]; auto expr = argv[1];
long val = 0; long val = 0;
if (EXPRESSION.parse_and_get_value(expr, val).ret) { if (EXPRESSION.parse_and_get_value(expr, val).ret) {
cout << expr << " = " << val << endl; std::cout << expr << " = " << val << std::endl;
return 0; return 0;
} }

View File

@ -10,16 +10,15 @@
#include <cstdlib> #include <cstdlib>
using namespace peg; using namespace peg;
using namespace std;
int main(int argc, const char** argv) int main(int argc, const char** argv)
{ {
if (argc < 2 || string("--help") == argv[1]) { if (argc < 2 || std::string("--help") == argv[1]) {
cout << "usage: calc3 [formula]" << endl; std::cout << "usage: calc3 [formula]" << std::endl;
return 1; return 1;
} }
function<long (const Ast&)> eval = [&](const Ast& ast) { std::function<long (const Ast&)> eval = [&](const Ast& ast) {
if (ast.name == "NUMBER") { if (ast.name == "NUMBER") {
return stol(ast.token); return stol(ast.token);
} else { } else {
@ -54,15 +53,15 @@ int main(int argc, const char** argv)
parser.enable_ast(); parser.enable_ast();
auto expr = argv[1]; auto expr = argv[1];
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
if (parser.parse(expr, ast)) { if (parser.parse(expr, ast)) {
ast = AstOptimizer(true).optimize(ast); ast = AstOptimizer(true).optimize(ast);
cout << ast_to_s(ast); std::cout << ast_to_s(ast);
cout << expr << " = " << eval(*ast) << endl; std::cout << expr << " = " << eval(*ast) << std::endl;
return 0; return 0;
} }
cout << "syntax error..." << endl; std::cout << "syntax error..." << std::endl;
return -1; return -1;
} }

172
peglib.h
View File

@ -8,6 +8,14 @@
#ifndef CPPPEGLIB_PEGLIB_H #ifndef CPPPEGLIB_PEGLIB_H
#define CPPPEGLIB_PEGLIB_H #define CPPPEGLIB_PEGLIB_H
#ifndef PEGLIB_USE_STD_ANY
#ifdef _MSVC_LANG
#define PEGLIB_USE_STD_ANY _MSVC_LANG >= 201703L
#elif defined(__cplusplus)
#define PEGLIB_USE_STD_ANY __cplusplus >= 201703L
#endif
#endif // PEGLIB_USE_STD_ANY
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cassert> #include <cassert>
@ -23,6 +31,9 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#if PEGLIB_USE_STD_ANY
#include <any>
#endif
// guard for older versions of VC++ // guard for older versions of VC++
#ifdef _MSC_VER #ifdef _MSC_VER
@ -45,7 +56,15 @@ namespace peg {
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* any * any
*---------------------------------------------------------------------------*/ *---------------------------------------------------------------------------*/
#if PEGLIB_USE_STD_ANY
using any = std::any;
// Define a function alias to std::any_cast using perfect forwarding
template <typename T, typename... Args>
auto any_cast( Args&&... args ) -> decltype(std::any_cast<T>(std::forward<Args>( args )... )) {
return std::any_cast<T>( std::forward<Args>( args )... );
}
#else
class any class any
{ {
public: public:
@ -85,55 +104,15 @@ public:
delete content_; delete content_;
} }
bool is_undefined() const { bool has_value() const {
return content_ == nullptr; return content_ != nullptr;
} }
template < template <typename T>
typename T, friend T& any_cast(any& val);
typename std::enable_if<!std::is_same<T, any>::value, std::nullptr_t>::type = nullptr
>
T& get() {
if (!content_) {
throw std::bad_cast();
}
auto p = dynamic_cast<holder<T>*>(content_);
assert(p);
if (!p) {
throw std::bad_cast();
}
return p->value_;
}
template < template <typename T>
typename T, friend const T& any_cast(const any& val);
typename std::enable_if<std::is_same<T, any>::value, std::nullptr_t>::type = nullptr
>
T& get() {
return *this;
}
template <
typename T,
typename std::enable_if<!std::is_same<T, any>::value, std::nullptr_t>::type = nullptr
>
const T& get() const {
assert(content_);
auto p = dynamic_cast<holder<T>*>(content_);
assert(p);
if (!p) {
throw std::bad_cast();
}
return p->value_;
}
template <
typename T,
typename std::enable_if<std::is_same<T, any>::value, std::nullptr_t>::type = nullptr
>
const any& get() const {
return *this;
}
private: private:
struct placeholder { struct placeholder {
@ -157,6 +136,41 @@ private:
placeholder* content_; placeholder* content_;
}; };
template <typename T>
T& any_cast(any& val) {
if (!val.content_) {
throw std::bad_cast();
}
auto p = dynamic_cast<any::holder<T>*>(val.content_);
assert(p);
if (!p) {
throw std::bad_cast();
}
return p->value_;
}
template <>
any& any_cast<any>(any& val) {
return val;
}
template <typename T>
const T& any_cast(const any& val) {
assert(val.content_);
auto p = dynamic_cast<any::holder<T>*>(val.content_);
assert(p);
if (!p) {
throw std::bad_cast();
}
return p->value_;
}
template <>
const any& any_cast<any>(const any& val) {
return val;
}
#endif
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* scope_exit * scope_exit
*---------------------------------------------------------------------------*/ *---------------------------------------------------------------------------*/
@ -499,7 +513,7 @@ struct SemanticValues : protected std::vector<any>
// Transform the semantic value vector to another vector // Transform the semantic value vector to another vector
template <typename T> template <typename T>
auto transform(size_t beg = 0, size_t end = static_cast<size_t>(-1)) const -> vector<T> { auto transform(size_t beg = 0, size_t end = static_cast<size_t>(-1)) const -> vector<T> {
return this->transform(beg, end, [](const any& v) { return v.get<T>(); }); return this->transform(beg, end, [](const any& v) { return any_cast<T>(v); });
} }
SemanticValues() : s_(nullptr), n_(0), choice_count_(0), choice_(0) {} SemanticValues() : s_(nullptr), n_(0), choice_count_(0), choice_(0) {}
@ -2061,8 +2075,8 @@ public:
SemanticValues sv; SemanticValues sv;
any dt; any dt;
auto r = parse_core(s, n, sv, dt, path); auto r = parse_core(s, n, sv, dt, path);
if (r.ret && !sv.empty() && !sv.front().is_undefined()) { if (r.ret && !sv.empty() && sv.front().has_value()) {
val = sv[0].get<T>(); val = any_cast<T>(sv[0]);
} }
return r; return r;
} }
@ -2077,8 +2091,8 @@ public:
Result parse_and_get_value(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const { Result parse_and_get_value(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const {
SemanticValues sv; SemanticValues sv;
auto r = parse_core(s, n, sv, dt, path); auto r = parse_core(s, n, sv, dt, path);
if (r.ret && !sv.empty() && !sv.front().is_undefined()) { if (r.ret && !sv.empty() && sv.front().has_value()) {
val = sv[0].get<T>(); val = any_cast<T>(sv[0]);
} }
return r; return r;
} }
@ -2645,19 +2659,19 @@ private:
void setup_actions() { void setup_actions() {
g["Definition"] = [&](const SemanticValues& sv, any& dt) { g["Definition"] = [&](const SemanticValues& sv, any& dt) {
auto is_macro = sv.choice() == 0; auto is_macro = sv.choice() == 0;
auto ignore = sv[0].get<bool>(); auto ignore = any_cast<bool>(sv[0]);
auto name = sv[1].get<std::string>(); auto name = any_cast<std::string>(sv[ 1 ]);
std::vector<std::string> params; std::vector<std::string> params;
std::shared_ptr<Ope> ope; std::shared_ptr<Ope> ope;
if (is_macro) { if (is_macro) {
params = sv[2].get<std::vector<std::string>>(); params = any_cast<std::vector<std::string>>(sv[2]);
ope = sv[4].get<std::shared_ptr<Ope>>(); ope = any_cast<std::shared_ptr<Ope>>(sv[4]);
} else { } else {
ope = sv[3].get<std::shared_ptr<Ope>>(); ope = any_cast<std::shared_ptr<Ope>>(sv[3]);
} }
Data& data = *dt.get<Data*>(); Data& data = *any_cast<Data*>(dt);
auto& grammar = *data.grammar; auto& grammar = *data.grammar;
if (!grammar.count(name)) { if (!grammar.count(name)) {
@ -2678,11 +2692,11 @@ private:
g["Expression"] = [&](const SemanticValues& sv) { g["Expression"] = [&](const SemanticValues& sv) {
if (sv.size() == 1) { if (sv.size() == 1) {
return sv[0].get<std::shared_ptr<Ope>>(); return any_cast<std::shared_ptr<Ope>>(sv[0]);
} else { } else {
std::vector<std::shared_ptr<Ope>> opes; std::vector<std::shared_ptr<Ope>> opes;
for (auto i = 0u; i < sv.size(); i++) { for (auto i = 0u; i < sv.size(); i++) {
opes.emplace_back(sv[i].get<std::shared_ptr<Ope>>()); opes.emplace_back(any_cast<std::shared_ptr<Ope>>(sv[i]));
} }
const std::shared_ptr<Ope> ope = std::make_shared<PrioritizedChoice>(opes); const std::shared_ptr<Ope> ope = std::make_shared<PrioritizedChoice>(opes);
return ope; return ope;
@ -2691,11 +2705,11 @@ private:
g["Sequence"] = [&](const SemanticValues& sv) { g["Sequence"] = [&](const SemanticValues& sv) {
if (sv.size() == 1) { if (sv.size() == 1) {
return sv[0].get<std::shared_ptr<Ope>>(); return any_cast<std::shared_ptr<Ope>>(sv[0]);
} else { } else {
std::vector<std::shared_ptr<Ope>> opes; std::vector<std::shared_ptr<Ope>> opes;
for (const auto& x: sv) { for (const auto& x: sv) {
opes.emplace_back(x.get<std::shared_ptr<Ope>>()); opes.emplace_back(any_cast<std::shared_ptr<Ope>>(x));
} }
const std::shared_ptr<Ope> ope = std::make_shared<Sequence>(opes); const std::shared_ptr<Ope> ope = std::make_shared<Sequence>(opes);
return ope; return ope;
@ -2705,11 +2719,11 @@ private:
g["Prefix"] = [&](const SemanticValues& sv) { g["Prefix"] = [&](const SemanticValues& sv) {
std::shared_ptr<Ope> ope; std::shared_ptr<Ope> ope;
if (sv.size() == 1) { if (sv.size() == 1) {
ope = sv[0].get<std::shared_ptr<Ope>>(); ope = any_cast<std::shared_ptr<Ope>>(sv[0]);
} else { } else {
assert(sv.size() == 2); assert(sv.size() == 2);
auto tok = sv[0].get<char>(); auto tok = any_cast<char>(sv[0]);
ope = sv[1].get<std::shared_ptr<Ope>>(); ope = any_cast<std::shared_ptr<Ope>>(sv[1]);
if (tok == '&') { if (tok == '&') {
ope = apd(ope); ope = apd(ope);
} else { // '!' } else { // '!'
@ -2720,12 +2734,12 @@ private:
}; };
g["Suffix"] = [&](const SemanticValues& sv) { g["Suffix"] = [&](const SemanticValues& sv) {
auto ope = sv[0].get<std::shared_ptr<Ope>>(); auto ope = any_cast<std::shared_ptr<Ope>>(sv[0]);
if (sv.size() == 1) { if (sv.size() == 1) {
return ope; return ope;
} else { } else {
assert(sv.size() == 2); assert(sv.size() == 2);
auto tok = sv[1].get<char>(); auto tok = any_cast<char>(sv[1]);
if (tok == '?') { if (tok == '?') {
return opt(ope); return opt(ope);
} else if (tok == '*') { } else if (tok == '*') {
@ -2737,18 +2751,18 @@ private:
}; };
g["Primary"] = [&](const SemanticValues& sv, any& dt) -> std::shared_ptr<Ope> { g["Primary"] = [&](const SemanticValues& sv, any& dt) -> std::shared_ptr<Ope> {
Data& data = *dt.get<Data*>(); Data& data = *any_cast<Data*>(dt);
switch (sv.choice()) { switch (sv.choice()) {
case 0: // Macro Reference case 0: // Macro Reference
case 1: { // Reference case 1: { // Reference
auto is_macro = sv.choice() == 0; auto is_macro = sv.choice() == 0;
auto ignore = sv[0].get<bool>(); auto ignore = any_cast<bool>(sv[0]);
const auto& ident = sv[1].get<std::string>(); const auto& ident = any_cast<std::string>(sv[1]);
std::vector<std::shared_ptr<Ope>> args; std::vector<std::shared_ptr<Ope>> args;
if (is_macro) { if (is_macro) {
args = sv[2].get<std::vector<std::shared_ptr<Ope>>>(); args = any_cast<std::vector<std::shared_ptr<Ope>>>(sv[2]);
} }
if (ignore) { if (ignore) {
@ -2758,23 +2772,23 @@ private:
} }
} }
case 2: { // (Expression) case 2: { // (Expression)
return sv[0].get<std::shared_ptr<Ope>>(); return any_cast<std::shared_ptr<Ope>>(sv[0]);
} }
case 3: { // TokenBoundary case 3: { // TokenBoundary
return tok(sv[0].get<std::shared_ptr<Ope>>()); return tok(any_cast<std::shared_ptr<Ope>>(sv[0]));
} }
case 4: { // CaptureScope case 4: { // CaptureScope
return csc(sv[0].get<std::shared_ptr<Ope>>()); return csc(any_cast<std::shared_ptr<Ope>>(sv[0]));
} }
case 5: { // Capture case 5: { // Capture
const auto& name = sv[0].get<std::string>(); const auto& name = any_cast<std::string>(sv[0]);
auto ope = sv[1].get<std::shared_ptr<Ope>>(); auto ope = any_cast<std::shared_ptr<Ope>>(sv[1]);
return cap(ope, [name](const char* a_s, size_t a_n, Context& c) { return cap(ope, [name](const char* a_s, size_t a_n, Context& c) {
c.capture_scope_stack.back()[name] = std::string(a_s, a_n); c.capture_scope_stack.back()[name] = std::string(a_s, a_n);
}); });
} }
default: { default: {
return sv[0].get<std::shared_ptr<Ope>>(); return any_cast<std::shared_ptr<Ope>>(sv[0]);
} }
} }
}; };
@ -2805,14 +2819,14 @@ private:
g["Range"] = [](const SemanticValues& sv) { g["Range"] = [](const SemanticValues& sv) {
switch (sv.choice()) { switch (sv.choice()) {
case 0: { case 0: {
auto s1 = sv[0].get<std::string>(); auto s1 = any_cast<std::string>(sv[0]);
auto s2 = sv[1].get<std::string>(); auto s2 = any_cast<std::string>(sv[1]);
auto cp1 = decode_codepoint(s1.c_str(), s1.length()); auto cp1 = decode_codepoint(s1.c_str(), s1.length());
auto cp2 = decode_codepoint(s2.c_str(), s2.length()); auto cp2 = decode_codepoint(s2.c_str(), s2.length());
return std::make_pair(cp1, cp2); return std::make_pair(cp1, cp2);
} }
case 1: { case 1: {
auto s = sv[0].get<std::string>(); auto s = any_cast<std::string>(sv[0]);
auto cp = decode_codepoint(s.c_str(), s.length()); auto cp = decode_codepoint(s.c_str(), s.length());
return std::make_pair(cp, cp); return std::make_pair(cp, cp);
} }

View File

@ -44,7 +44,7 @@ TEST_CASE("Action taking non const Semantic Values parameter", "[general]")
)"); )");
parser["ROOT"] = [&](peg::SemanticValues& sv) { parser["ROOT"] = [&](peg::SemanticValues& sv) {
auto& s = sv[0].get<std::string>(); auto s = peg::any_cast<std::string>(sv[0]);
s[0] = 'H'; // mutate s[0] = 'H'; // mutate
return std::string(std::move(s)); // move return std::string(std::move(s)); // move
}; };
@ -83,11 +83,10 @@ TEST_CASE("String capture test", "[general]")
} }
using namespace peg; using namespace peg;
using namespace std;
TEST_CASE("String capture test2", "[general]") TEST_CASE("String capture test2", "[general]")
{ {
vector<string> tags; std::vector<std::string> tags;
Definition ROOT, TAG, TAG_NAME, WS; Definition ROOT, TAG, TAG_NAME, WS;
ROOT <= seq(WS, zom(TAG)); ROOT <= seq(WS, zom(TAG));
@ -178,7 +177,7 @@ TEST_CASE("Lambda action test", "[general]")
CHAR <- . CHAR <- .
)"); )");
string ss; std::string ss;
parser["CHAR"] = [&](const SemanticValues& sv) { parser["CHAR"] = [&](const SemanticValues& sv) {
ss += *sv.c_str(); ss += *sv.c_str();
}; };
@ -198,18 +197,18 @@ TEST_CASE("enter/leave handlers test", "[general]")
)"); )");
parser["LTOKEN"].enter = [&](const char*, size_t, any& dt) { parser["LTOKEN"].enter = [&](const char*, size_t, any& dt) {
auto& require_upper_case = *dt.get<bool*>(); auto& require_upper_case = *any_cast<bool*>(dt);
require_upper_case = false; require_upper_case = false;
}; };
parser["LTOKEN"].leave = [&](const char*, size_t, size_t, any&, any& dt) { parser["LTOKEN"].leave = [&](const char*, size_t, size_t, any&, any& dt) {
auto& require_upper_case = *dt.get<bool*>(); auto& require_upper_case = *any_cast<bool*>(dt);
require_upper_case = true; require_upper_case = true;
}; };
auto message = "should be upper case string..."; auto message = "should be upper case string...";
parser["TOKEN"] = [&](const SemanticValues& sv, any& dt) { parser["TOKEN"] = [&](const SemanticValues& sv, any& dt) {
auto& require_upper_case = *dt.get<bool*>(); auto& require_upper_case = *any_cast<bool*>(dt);
if (require_upper_case) { if (require_upper_case) {
const auto& s = sv.str(); const auto& s = sv.str();
if (!std::all_of(s.begin(), s.end(), ::isupper)) { if (!std::all_of(s.begin(), s.end(), ::isupper)) {
@ -225,7 +224,7 @@ TEST_CASE("enter/leave handlers test", "[general]")
REQUIRE(parser.parse("hello=WORLD", dt) == true); REQUIRE(parser.parse("hello=WORLD", dt) == true);
REQUIRE(parser.parse("HELLO=WORLD", dt) == true); REQUIRE(parser.parse("HELLO=WORLD", dt) == true);
parser.log = [&](size_t ln, size_t col, const string& msg) { parser.log = [&](size_t ln, size_t col, const std::string& msg) {
REQUIRE(ln == 1); REQUIRE(ln == 1);
REQUIRE(col == 7); REQUIRE(col == 7);
REQUIRE(msg == message); REQUIRE(msg == message);
@ -264,7 +263,7 @@ TEST_CASE("WHITESPACE test2", "[general]")
TAB <- '\t' TAB <- '\t'
)"); )");
vector<string> items; std::vector<std::string> items;
parser["ITEM"] = [&](const SemanticValues& sv) { parser["ITEM"] = [&](const SemanticValues& sv) {
items.push_back(sv.token()); items.push_back(sv.token());
}; };
@ -398,7 +397,7 @@ TEST_CASE("Backtracking with AST", "[general]")
)"); )");
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
bool ret = parser.parse("ba", ast); bool ret = parser.parse("ba", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
REQUIRE(ast->nodes.size() == 2); REQUIRE(ast->nodes.size() == 2);
@ -437,7 +436,7 @@ TEST_CASE("Ignore case test", "[general]") {
TEST_CASE("mutable lambda test", "[general]") TEST_CASE("mutable lambda test", "[general]")
{ {
vector<string> vec; std::vector<std::string> vec;
parser pg("ROOT <- 'mutable lambda test'"); parser pg("ROOT <- 'mutable lambda test'");
@ -459,18 +458,18 @@ TEST_CASE("Simple calculator test", "[general]")
parser["Additive"] = [](const SemanticValues& sv) { parser["Additive"] = [](const SemanticValues& sv) {
switch (sv.choice()) { switch (sv.choice()) {
case 0: case 0:
return sv[0].get<int>() + sv[1].get<int>(); return any_cast<int>(sv[0]) + any_cast<int>(sv[1]);
default: default:
return sv[0].get<int>(); return any_cast<int>(sv[0]);
} }
}; };
parser["Multitive"] = [](const SemanticValues& sv) { parser["Multitive"] = [](const SemanticValues& sv) {
switch (sv.choice()) { switch (sv.choice()) {
case 0: case 0:
return sv[0].get<int>() * sv[1].get<int>(); return any_cast<int>(sv[0]) * any_cast<int>(sv[1]);
default: default:
return sv[0].get<int>(); return any_cast<int>(sv[0]);
} }
}; };
@ -498,10 +497,10 @@ TEST_CASE("Calculator test", "[general]")
// Setup actions // Setup actions
auto reduce = [](const SemanticValues& sv) -> long { auto reduce = [](const SemanticValues& sv) -> long {
long ret = sv[0].get<long>(); long ret = any_cast<long>(sv[0]);
for (auto i = 1u; i < sv.size(); i += 2) { for (auto i = 1u; i < sv.size(); i += 2) {
auto num = sv[i + 1].get<long>(); auto num = any_cast<long>(sv[i + 1]);
switch (sv[i].get<char>()) { switch (any_cast<char>(sv[i])) {
case '+': ret += num; break; case '+': ret += num; break;
case '-': ret -= num; break; case '-': ret -= num; break;
case '*': ret *= num; break; case '*': ret *= num; break;
@ -538,16 +537,16 @@ TEST_CASE("Calculator test2", "[general]")
NUMBER <- [0-9]+ NUMBER <- [0-9]+
)"; )";
string start; std::string start;
auto grammar = ParserGenerator::parse(syntax, strlen(syntax), start, nullptr); auto grammar = ParserGenerator::parse(syntax, strlen(syntax), start, nullptr);
auto& g = *grammar; auto& g = *grammar;
// Setup actions // Setup actions
auto reduce = [](const SemanticValues& sv) -> long { auto reduce = [](const SemanticValues& sv) -> long {
long ret = sv[0].get<long>(); long ret = any_cast<long>(sv[0]);
for (auto i = 1u; i < sv.size(); i += 2) { for (auto i = 1u; i < sv.size(); i += 2) {
auto num = sv[i + 1].get<long>(); auto num = any_cast<long>(sv[i + 1]);
switch (sv[i].get<char>()) { switch (any_cast<char>(sv[i])) {
case '+': ret += num; break; case '+': ret += num; break;
case '-': ret -= num; break; case '-': ret -= num; break;
case '*': ret *= num; break; case '*': ret *= num; break;
@ -585,10 +584,10 @@ TEST_CASE("Calculator test3", "[general]")
)"); )");
auto reduce = [](const SemanticValues& sv) -> long { auto reduce = [](const SemanticValues& sv) -> long {
long ret = sv[0].get<long>(); long ret = any_cast<long>(sv[0]);
for (auto i = 1u; i < sv.size(); i += 2) { for (auto i = 1u; i < sv.size(); i += 2) {
auto num = sv[i + 1].get<long>(); auto num = any_cast<long>(sv[i + 1]);
switch (sv[i].get<char>()) { switch (any_cast<char>(sv[i])) {
case '+': ret += num; break; case '+': ret += num; break;
case '-': ret -= num; break; case '-': ret -= num; break;
case '*': ret *= num; break; case '*': ret *= num; break;
@ -627,7 +626,7 @@ TEST_CASE("Calculator test with AST", "[general]")
parser.enable_ast(); parser.enable_ast();
function<long (const Ast&)> eval = [&](const Ast& ast) { std::function<long (const Ast&)> eval = [&](const Ast& ast) {
if (ast.name == "NUMBER") { if (ast.name == "NUMBER") {
return stol(ast.token); return stol(ast.token);
} else { } else {
@ -647,7 +646,7 @@ TEST_CASE("Calculator test with AST", "[general]")
} }
}; };
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse("1+2*3*(4-5+6)/7-8", ast); auto ret = parser.parse("1+2*3*(4-5+6)/7-8", ast);
ast = peg::AstOptimizer(true).optimize(ast); ast = peg::AstOptimizer(true).optimize(ast);
auto val = eval(*ast); auto val = eval(*ast);
@ -667,7 +666,7 @@ TEST_CASE("Ignore semantic value test", "[general]")
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse("Hello World", ast); auto ret = parser.parse("Hello World", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
@ -687,7 +686,7 @@ TEST_CASE("Ignore semantic value of 'or' predicate test", "[general]")
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse("Hello World.", ast); auto ret = parser.parse("Hello World.", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
@ -706,7 +705,7 @@ TEST_CASE("Ignore semantic value of 'and' predicate test", "[general]")
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse("Hello World.", ast); auto ret = parser.parse("Hello World.", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
@ -721,7 +720,7 @@ TEST_CASE("Literal token on AST test1", "[general]")
)"); )");
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse(R"("a\tb")", ast); auto ret = parser.parse(R"("a\tb")", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
@ -739,7 +738,7 @@ TEST_CASE("Literal token on AST test2", "[general]")
)"); )");
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse(R"("a\tb")", ast); auto ret = parser.parse(R"("a\tb")", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
@ -757,7 +756,7 @@ TEST_CASE("Literal token on AST test3", "[general]")
)"); )");
parser.enable_ast(); parser.enable_ast();
shared_ptr<Ast> ast; std::shared_ptr<Ast> ast;
auto ret = parser.parse(R"("a\tb")", ast); auto ret = parser.parse(R"("a\tb")", ast);
REQUIRE(ret == true); REQUIRE(ret == true);
@ -798,12 +797,12 @@ TEST_CASE("Semantic values test", "[general]")
for (const auto& rule: parser.get_rule_names()){ for (const auto& rule: parser.get_rule_names()){
parser[rule.c_str()] = [rule](const SemanticValues& sv, any&) { parser[rule.c_str()] = [rule](const SemanticValues& sv, any&) {
if (rule == "term") { if (rule == "term") {
REQUIRE(sv[0].get<string>() == "a at 0"); REQUIRE(any_cast<std::string>(sv[0]) == "a at 0");
REQUIRE(sv[1].get<string>() == "b at 1"); REQUIRE(any_cast<std::string>(sv[1]) == "b at 1");
REQUIRE(sv[2].get<string>() == "c at 2"); REQUIRE(any_cast<std::string>(sv[2]) == "c at 2");
return string(); return std::string();
} else { } else {
return rule + " at " + to_string(sv.c_str() - sv.ss); return rule + " at " + std::to_string(sv.c_str() - sv.ss);
} }
}; };
} }
@ -1149,7 +1148,7 @@ TEST_CASE("User defined rule test", "[user rule]")
{ {
{ {
"NAME", usr([](const char* s, size_t n, SemanticValues& /*sv*/, any& /*dt*/) -> size_t { "NAME", usr([](const char* s, size_t n, SemanticValues& /*sv*/, any& /*dt*/) -> size_t {
static vector<string> names = { "PEG", "BNF" }; static std::vector<std::string> names = { "PEG", "BNF" };
for (const auto& name: names) { for (const auto& name: names) {
if (name.size() <= n && !name.compare(0, name.size(), s, name.size())) { if (name.size() <= n && !name.compare(0, name.size(), s, name.size())) {
return name.size(); return name.size();
@ -1336,10 +1335,10 @@ TEST_CASE("Macro calculator", "[macro]")
// Setup actions // Setup actions
auto reduce = [](const SemanticValues& sv) -> long { auto reduce = [](const SemanticValues& sv) -> long {
auto result = sv[0].get<long>(); auto result = any_cast<long>(sv[0]);
for (auto i = 1u; i < sv.size(); i += 2) { for (auto i = 1u; i < sv.size(); i += 2) {
auto num = sv[i + 1].get<long>(); auto num = any_cast<long>(sv[i + 1]);
auto ope = sv[i].get<char>(); auto ope = any_cast<char>(sv[i]);
switch (ope) { switch (ope) {
case '+': result += num; break; case '+': result += num; break;
case '-': result -= num; break; case '-': result -= num; break;
@ -1462,7 +1461,7 @@ TEST_CASE("Line information test", "[line information]")
~_ <- [ \t\r\n]+ ~_ <- [ \t\r\n]+
)"); )");
std::vector<std::pair<int, int>> locations; std::vector<std::pair<size_t, size_t>> locations;
parser["WORD"] = [&](const peg::SemanticValues& sv) { parser["WORD"] = [&](const peg::SemanticValues& sv) {
locations.push_back(sv.line_info()); locations.push_back(sv.line_info());
}; };
@ -1473,13 +1472,13 @@ TEST_CASE("Line information test", "[line information]")
ret = parser.parse(" Mon Tue Wed \nThu Fri Sat\nSun\n"); ret = parser.parse(" Mon Tue Wed \nThu Fri Sat\nSun\n");
REQUIRE(ret == true); REQUIRE(ret == true);
REQUIRE(locations[0] == std::make_pair(1, 2)); REQUIRE(locations[0] == std::make_pair<size_t, size_t>(1, 2));
REQUIRE(locations[1] == std::make_pair(1, 6)); REQUIRE(locations[1] == std::make_pair<size_t, size_t>(1, 6));
REQUIRE(locations[2] == std::make_pair(1, 10)); REQUIRE(locations[2] == std::make_pair<size_t, size_t>(1, 10));
REQUIRE(locations[3] == std::make_pair(2, 1)); REQUIRE(locations[3] == std::make_pair<size_t, size_t>(2, 1));
REQUIRE(locations[4] == std::make_pair(2, 6)); REQUIRE(locations[4] == std::make_pair<size_t, size_t>(2, 6));
REQUIRE(locations[5] == std::make_pair(2, 11)); REQUIRE(locations[5] == std::make_pair<size_t, size_t>(2, 11));
REQUIRE(locations[6] == std::make_pair(3, 1)); REQUIRE(locations[6] == std::make_pair<size_t, size_t>(3, 1));
} }
bool exact(Grammar& g, const char* d, const char* s) { bool exact(Grammar& g, const char* d, const char* s) {