mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Merge pull request #72 from romeoxbm/master
Replace peg::any with std::any
This commit is contained in:
commit
666b364425
@ -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:
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
|
||||
message(FATAL_ERROR "Need at least gcc 4.9 to compile.")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(MSVC)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19)
|
||||
message(FATAL_ERROR "Visual Studio 2015 or newer is required.")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
|
||||
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")
|
||||
# Check if a supported compiler is used to setup the C++ standard to use:
|
||||
get_property(known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
list(FIND known_features "cxx_std_17" found)
|
||||
if(NOT ${found} EQUAL -1)
|
||||
# C++17 standard is supported
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
else()
|
||||
message(FATAL_ERROR "Your compiler doesn't support the '-std=c++11' flag.")
|
||||
# Check for C++11 standard support
|
||||
list(FIND known_features "cxx_std_11" found)
|
||||
if(NOT ${found} EQUAL -1)
|
||||
# C++11 standard is supported
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
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"
|
||||
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
|
@ -111,6 +111,13 @@ 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.
|
||||
|
||||
`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.)
|
||||
|
||||
|
@ -10,20 +10,19 @@
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc < 2 || string("--help") == argv[1]) {
|
||||
cout << "usage: calc [formula]" << endl;
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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) {
|
||||
auto num = sv[i + 1].get<long>();
|
||||
auto ope = sv[i].get<char>();
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
auto ope = any_cast<char>(sv[i]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
@ -53,11 +52,11 @@ int main(int argc, const char** argv)
|
||||
auto expr = argv[1];
|
||||
long val = 0;
|
||||
if (parser.parse(expr, val)) {
|
||||
cout << expr << " = " << val << endl;
|
||||
std::cout << expr << " = " << val << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cout << "syntax error..." << endl;
|
||||
std::cout << "syntax error..." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
//
|
||||
// PEG syntax:
|
||||
@ -24,16 +23,16 @@ using namespace std;
|
||||
//
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc < 2 || string("--help") == argv[1]) {
|
||||
cout << "usage: calc [formula]" << endl;
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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) {
|
||||
auto num = sv[i + 1].get<long>();
|
||||
auto ope = sv[i].get<char>();
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
auto ope = any_cast<char>(sv[i]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
@ -56,7 +55,7 @@ int main(int argc, const char** argv)
|
||||
auto expr = argv[1];
|
||||
long val = 0;
|
||||
if (EXPRESSION.parse_and_get_value(expr, val).ret) {
|
||||
cout << expr << " = " << val << endl;
|
||||
std::cout << expr << " = " << val << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -10,16 +10,15 @@
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc < 2 || string("--help") == argv[1]) {
|
||||
cout << "usage: calc3 [formula]" << endl;
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc3 [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||
std::function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||
if (ast.name == "NUMBER") {
|
||||
return stol(ast.token);
|
||||
} else {
|
||||
@ -54,15 +53,15 @@ int main(int argc, const char** argv)
|
||||
parser.enable_ast();
|
||||
|
||||
auto expr = argv[1];
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
cout << ast_to_s(ast);
|
||||
cout << expr << " = " << eval(*ast) << endl;
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cout << "syntax error..." << endl;
|
||||
std::cout << "syntax error..." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
172
peglib.h
172
peglib.h
@ -8,6 +8,14 @@
|
||||
#ifndef 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 <cctype>
|
||||
#include <cassert>
|
||||
@ -23,6 +31,9 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#if PEGLIB_USE_STD_ANY
|
||||
#include <any>
|
||||
#endif
|
||||
|
||||
// guard for older versions of VC++
|
||||
#ifdef _MSC_VER
|
||||
@ -45,7 +56,15 @@ namespace peg {
|
||||
/*-----------------------------------------------------------------------------
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
@ -85,55 +104,15 @@ public:
|
||||
delete content_;
|
||||
}
|
||||
|
||||
bool is_undefined() const {
|
||||
return content_ == nullptr;
|
||||
bool has_value() const {
|
||||
return content_ != nullptr;
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
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 <typename T>
|
||||
friend T& any_cast(any& val);
|
||||
|
||||
template <
|
||||
typename T,
|
||||
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;
|
||||
}
|
||||
template <typename T>
|
||||
friend const T& any_cast(const any& val);
|
||||
|
||||
private:
|
||||
struct placeholder {
|
||||
@ -157,6 +136,41 @@ private:
|
||||
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
|
||||
*---------------------------------------------------------------------------*/
|
||||
@ -499,7 +513,7 @@ struct SemanticValues : protected std::vector<any>
|
||||
// Transform the semantic value vector to another vector
|
||||
template <typename 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) {}
|
||||
@ -2061,8 +2075,8 @@ public:
|
||||
SemanticValues sv;
|
||||
any dt;
|
||||
auto r = parse_core(s, n, sv, dt, path);
|
||||
if (r.ret && !sv.empty() && !sv.front().is_undefined()) {
|
||||
val = sv[0].get<T>();
|
||||
if (r.ret && !sv.empty() && sv.front().has_value()) {
|
||||
val = any_cast<T>(sv[0]);
|
||||
}
|
||||
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 {
|
||||
SemanticValues sv;
|
||||
auto r = parse_core(s, n, sv, dt, path);
|
||||
if (r.ret && !sv.empty() && !sv.front().is_undefined()) {
|
||||
val = sv[0].get<T>();
|
||||
if (r.ret && !sv.empty() && sv.front().has_value()) {
|
||||
val = any_cast<T>(sv[0]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@ -2645,19 +2659,19 @@ private:
|
||||
void setup_actions() {
|
||||
g["Definition"] = [&](const SemanticValues& sv, any& dt) {
|
||||
auto is_macro = sv.choice() == 0;
|
||||
auto ignore = sv[0].get<bool>();
|
||||
auto name = sv[1].get<std::string>();
|
||||
auto ignore = any_cast<bool>(sv[0]);
|
||||
auto name = any_cast<std::string>(sv[ 1 ]);
|
||||
|
||||
std::vector<std::string> params;
|
||||
std::shared_ptr<Ope> ope;
|
||||
if (is_macro) {
|
||||
params = sv[2].get<std::vector<std::string>>();
|
||||
ope = sv[4].get<std::shared_ptr<Ope>>();
|
||||
params = any_cast<std::vector<std::string>>(sv[2]);
|
||||
ope = any_cast<std::shared_ptr<Ope>>(sv[4]);
|
||||
} 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;
|
||||
if (!grammar.count(name)) {
|
||||
@ -2678,11 +2692,11 @@ private:
|
||||
|
||||
g["Expression"] = [&](const SemanticValues& sv) {
|
||||
if (sv.size() == 1) {
|
||||
return sv[0].get<std::shared_ptr<Ope>>();
|
||||
return any_cast<std::shared_ptr<Ope>>(sv[0]);
|
||||
} else {
|
||||
std::vector<std::shared_ptr<Ope>> opes;
|
||||
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);
|
||||
return ope;
|
||||
@ -2691,11 +2705,11 @@ private:
|
||||
|
||||
g["Sequence"] = [&](const SemanticValues& sv) {
|
||||
if (sv.size() == 1) {
|
||||
return sv[0].get<std::shared_ptr<Ope>>();
|
||||
return any_cast<std::shared_ptr<Ope>>(sv[0]);
|
||||
} else {
|
||||
std::vector<std::shared_ptr<Ope>> opes;
|
||||
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);
|
||||
return ope;
|
||||
@ -2705,11 +2719,11 @@ private:
|
||||
g["Prefix"] = [&](const SemanticValues& sv) {
|
||||
std::shared_ptr<Ope> ope;
|
||||
if (sv.size() == 1) {
|
||||
ope = sv[0].get<std::shared_ptr<Ope>>();
|
||||
ope = any_cast<std::shared_ptr<Ope>>(sv[0]);
|
||||
} else {
|
||||
assert(sv.size() == 2);
|
||||
auto tok = sv[0].get<char>();
|
||||
ope = sv[1].get<std::shared_ptr<Ope>>();
|
||||
auto tok = any_cast<char>(sv[0]);
|
||||
ope = any_cast<std::shared_ptr<Ope>>(sv[1]);
|
||||
if (tok == '&') {
|
||||
ope = apd(ope);
|
||||
} else { // '!'
|
||||
@ -2720,12 +2734,12 @@ private:
|
||||
};
|
||||
|
||||
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) {
|
||||
return ope;
|
||||
} else {
|
||||
assert(sv.size() == 2);
|
||||
auto tok = sv[1].get<char>();
|
||||
auto tok = any_cast<char>(sv[1]);
|
||||
if (tok == '?') {
|
||||
return opt(ope);
|
||||
} else if (tok == '*') {
|
||||
@ -2737,18 +2751,18 @@ private:
|
||||
};
|
||||
|
||||
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()) {
|
||||
case 0: // Macro Reference
|
||||
case 1: { // Reference
|
||||
auto is_macro = sv.choice() == 0;
|
||||
auto ignore = sv[0].get<bool>();
|
||||
const auto& ident = sv[1].get<std::string>();
|
||||
auto ignore = any_cast<bool>(sv[0]);
|
||||
const auto& ident = any_cast<std::string>(sv[1]);
|
||||
|
||||
std::vector<std::shared_ptr<Ope>> args;
|
||||
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) {
|
||||
@ -2758,23 +2772,23 @@ private:
|
||||
}
|
||||
}
|
||||
case 2: { // (Expression)
|
||||
return sv[0].get<std::shared_ptr<Ope>>();
|
||||
return any_cast<std::shared_ptr<Ope>>(sv[0]);
|
||||
}
|
||||
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
|
||||
return csc(sv[0].get<std::shared_ptr<Ope>>());
|
||||
return csc(any_cast<std::shared_ptr<Ope>>(sv[0]));
|
||||
}
|
||||
case 5: { // Capture
|
||||
const auto& name = sv[0].get<std::string>();
|
||||
auto ope = sv[1].get<std::shared_ptr<Ope>>();
|
||||
const auto& name = any_cast<std::string>(sv[0]);
|
||||
auto ope = any_cast<std::shared_ptr<Ope>>(sv[1]);
|
||||
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);
|
||||
});
|
||||
}
|
||||
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) {
|
||||
switch (sv.choice()) {
|
||||
case 0: {
|
||||
auto s1 = sv[0].get<std::string>();
|
||||
auto s2 = sv[1].get<std::string>();
|
||||
auto s1 = any_cast<std::string>(sv[0]);
|
||||
auto s2 = any_cast<std::string>(sv[1]);
|
||||
auto cp1 = decode_codepoint(s1.c_str(), s1.length());
|
||||
auto cp2 = decode_codepoint(s2.c_str(), s2.length());
|
||||
return std::make_pair(cp1, cp2);
|
||||
}
|
||||
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());
|
||||
return std::make_pair(cp, cp);
|
||||
}
|
||||
|
99
test/test.cc
99
test/test.cc
@ -44,7 +44,7 @@ TEST_CASE("Action taking non const Semantic Values parameter", "[general]")
|
||||
)");
|
||||
|
||||
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
|
||||
return std::string(std::move(s)); // move
|
||||
};
|
||||
@ -83,11 +83,10 @@ TEST_CASE("String capture test", "[general]")
|
||||
}
|
||||
|
||||
using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
TEST_CASE("String capture test2", "[general]")
|
||||
{
|
||||
vector<string> tags;
|
||||
std::vector<std::string> tags;
|
||||
|
||||
Definition ROOT, TAG, TAG_NAME, WS;
|
||||
ROOT <= seq(WS, zom(TAG));
|
||||
@ -178,7 +177,7 @@ TEST_CASE("Lambda action test", "[general]")
|
||||
CHAR <- .
|
||||
)");
|
||||
|
||||
string ss;
|
||||
std::string ss;
|
||||
parser["CHAR"] = [&](const SemanticValues& sv) {
|
||||
ss += *sv.c_str();
|
||||
};
|
||||
@ -198,18 +197,18 @@ TEST_CASE("enter/leave handlers test", "[general]")
|
||||
)");
|
||||
|
||||
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;
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
auto message = "should be upper case string...";
|
||||
|
||||
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) {
|
||||
const auto& s = sv.str();
|
||||
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);
|
||||
|
||||
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(col == 7);
|
||||
REQUIRE(msg == message);
|
||||
@ -264,7 +263,7 @@ TEST_CASE("WHITESPACE test2", "[general]")
|
||||
TAB <- '\t'
|
||||
)");
|
||||
|
||||
vector<string> items;
|
||||
std::vector<std::string> items;
|
||||
parser["ITEM"] = [&](const SemanticValues& sv) {
|
||||
items.push_back(sv.token());
|
||||
};
|
||||
@ -398,7 +397,7 @@ TEST_CASE("Backtracking with AST", "[general]")
|
||||
)");
|
||||
|
||||
parser.enable_ast();
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
bool ret = parser.parse("ba", ast);
|
||||
REQUIRE(ret == true);
|
||||
REQUIRE(ast->nodes.size() == 2);
|
||||
@ -437,7 +436,7 @@ TEST_CASE("Ignore case test", "[general]") {
|
||||
|
||||
TEST_CASE("mutable lambda test", "[general]")
|
||||
{
|
||||
vector<string> vec;
|
||||
std::vector<std::string> vec;
|
||||
|
||||
parser pg("ROOT <- 'mutable lambda test'");
|
||||
|
||||
@ -459,18 +458,18 @@ TEST_CASE("Simple calculator test", "[general]")
|
||||
parser["Additive"] = [](const SemanticValues& sv) {
|
||||
switch (sv.choice()) {
|
||||
case 0:
|
||||
return sv[0].get<int>() + sv[1].get<int>();
|
||||
return any_cast<int>(sv[0]) + any_cast<int>(sv[1]);
|
||||
default:
|
||||
return sv[0].get<int>();
|
||||
return any_cast<int>(sv[0]);
|
||||
}
|
||||
};
|
||||
|
||||
parser["Multitive"] = [](const SemanticValues& sv) {
|
||||
switch (sv.choice()) {
|
||||
case 0:
|
||||
return sv[0].get<int>() * sv[1].get<int>();
|
||||
return any_cast<int>(sv[0]) * any_cast<int>(sv[1]);
|
||||
default:
|
||||
return sv[0].get<int>();
|
||||
return any_cast<int>(sv[0]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -498,10 +497,10 @@ TEST_CASE("Calculator test", "[general]")
|
||||
|
||||
// Setup actions
|
||||
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) {
|
||||
auto num = sv[i + 1].get<long>();
|
||||
switch (sv[i].get<char>()) {
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
switch (any_cast<char>(sv[i])) {
|
||||
case '+': ret += num; break;
|
||||
case '-': ret -= num; break;
|
||||
case '*': ret *= num; break;
|
||||
@ -538,16 +537,16 @@ TEST_CASE("Calculator test2", "[general]")
|
||||
NUMBER <- [0-9]+
|
||||
)";
|
||||
|
||||
string start;
|
||||
std::string start;
|
||||
auto grammar = ParserGenerator::parse(syntax, strlen(syntax), start, nullptr);
|
||||
auto& g = *grammar;
|
||||
|
||||
// Setup actions
|
||||
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) {
|
||||
auto num = sv[i + 1].get<long>();
|
||||
switch (sv[i].get<char>()) {
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
switch (any_cast<char>(sv[i])) {
|
||||
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 {
|
||||
long ret = sv[0].get<long>();
|
||||
long ret = any_cast<long>(sv[0]);
|
||||
for (auto i = 1u; i < sv.size(); i += 2) {
|
||||
auto num = sv[i + 1].get<long>();
|
||||
switch (sv[i].get<char>()) {
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
switch (any_cast<char>(sv[i])) {
|
||||
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();
|
||||
|
||||
function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||
std::function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||
if (ast.name == "NUMBER") {
|
||||
return stol(ast.token);
|
||||
} 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);
|
||||
ast = peg::AstOptimizer(true).optimize(ast);
|
||||
auto val = eval(*ast);
|
||||
@ -667,7 +666,7 @@ TEST_CASE("Ignore semantic value test", "[general]")
|
||||
|
||||
parser.enable_ast();
|
||||
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
auto ret = parser.parse("Hello World", ast);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
@ -687,7 +686,7 @@ TEST_CASE("Ignore semantic value of 'or' predicate test", "[general]")
|
||||
|
||||
parser.enable_ast();
|
||||
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
auto ret = parser.parse("Hello World.", ast);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
@ -706,7 +705,7 @@ TEST_CASE("Ignore semantic value of 'and' predicate test", "[general]")
|
||||
|
||||
parser.enable_ast();
|
||||
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
auto ret = parser.parse("Hello World.", ast);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
@ -721,7 +720,7 @@ TEST_CASE("Literal token on AST test1", "[general]")
|
||||
)");
|
||||
parser.enable_ast();
|
||||
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
auto ret = parser.parse(R"("a\tb")", ast);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
@ -739,7 +738,7 @@ TEST_CASE("Literal token on AST test2", "[general]")
|
||||
)");
|
||||
parser.enable_ast();
|
||||
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
auto ret = parser.parse(R"("a\tb")", ast);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
@ -757,7 +756,7 @@ TEST_CASE("Literal token on AST test3", "[general]")
|
||||
)");
|
||||
parser.enable_ast();
|
||||
|
||||
shared_ptr<Ast> ast;
|
||||
std::shared_ptr<Ast> ast;
|
||||
auto ret = parser.parse(R"("a\tb")", ast);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
@ -798,12 +797,12 @@ TEST_CASE("Semantic values test", "[general]")
|
||||
for (const auto& rule: parser.get_rule_names()){
|
||||
parser[rule.c_str()] = [rule](const SemanticValues& sv, any&) {
|
||||
if (rule == "term") {
|
||||
REQUIRE(sv[0].get<string>() == "a at 0");
|
||||
REQUIRE(sv[1].get<string>() == "b at 1");
|
||||
REQUIRE(sv[2].get<string>() == "c at 2");
|
||||
return string();
|
||||
REQUIRE(any_cast<std::string>(sv[0]) == "a at 0");
|
||||
REQUIRE(any_cast<std::string>(sv[1]) == "b at 1");
|
||||
REQUIRE(any_cast<std::string>(sv[2]) == "c at 2");
|
||||
return std::string();
|
||||
} 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 {
|
||||
static vector<string> names = { "PEG", "BNF" };
|
||||
static std::vector<std::string> names = { "PEG", "BNF" };
|
||||
for (const auto& name: names) {
|
||||
if (name.size() <= n && !name.compare(0, name.size(), s, name.size())) {
|
||||
return name.size();
|
||||
@ -1336,10 +1335,10 @@ TEST_CASE("Macro calculator", "[macro]")
|
||||
|
||||
// Setup actions
|
||||
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) {
|
||||
auto num = sv[i + 1].get<long>();
|
||||
auto ope = sv[i].get<char>();
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
auto ope = any_cast<char>(sv[i]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
@ -1462,7 +1461,7 @@ TEST_CASE("Line information test", "[line information]")
|
||||
~_ <- [ \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) {
|
||||
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");
|
||||
REQUIRE(ret == true);
|
||||
|
||||
REQUIRE(locations[0] == std::make_pair(1, 2));
|
||||
REQUIRE(locations[1] == std::make_pair(1, 6));
|
||||
REQUIRE(locations[2] == std::make_pair(1, 10));
|
||||
REQUIRE(locations[3] == std::make_pair(2, 1));
|
||||
REQUIRE(locations[4] == std::make_pair(2, 6));
|
||||
REQUIRE(locations[5] == std::make_pair(2, 11));
|
||||
REQUIRE(locations[6] == std::make_pair(3, 1));
|
||||
REQUIRE(locations[0] == std::make_pair<size_t, size_t>(1, 2));
|
||||
REQUIRE(locations[1] == std::make_pair<size_t, size_t>(1, 6));
|
||||
REQUIRE(locations[2] == std::make_pair<size_t, size_t>(1, 10));
|
||||
REQUIRE(locations[3] == std::make_pair<size_t, size_t>(2, 1));
|
||||
REQUIRE(locations[4] == std::make_pair<size_t, size_t>(2, 6));
|
||||
REQUIRE(locations[5] == std::make_pair<size_t, size_t>(2, 11));
|
||||
REQUIRE(locations[6] == std::make_pair<size_t, size_t>(3, 1));
|
||||
}
|
||||
|
||||
bool exact(Grammar& g, const char* d, const char* s) {
|
||||
|
Loading…
Reference in New Issue
Block a user