mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
Implement non-throwing version of x3::expect
This commit is contained in:
@@ -78,7 +78,8 @@ run difference.cpp ;
|
||||
run eoi.cpp ;
|
||||
run eol.cpp ;
|
||||
run eps.cpp ;
|
||||
run expect.cpp ;
|
||||
run expect_throw.cpp ;
|
||||
run expect_nothrow.cpp ;
|
||||
run extract_int.cpp ;
|
||||
run int1.cpp ;
|
||||
run kleene.cpp ;
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2013 Joel de Guzman
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
=============================================================================*/
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#include <boost/fusion/include/vector.hpp>
|
||||
#include <boost/fusion/include/at.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "test.hpp"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace boost::spirit;
|
||||
using namespace boost::spirit::x3::ascii;
|
||||
using boost::spirit::x3::lit;
|
||||
using boost::spirit::x3::expect;
|
||||
using spirit_test::test;
|
||||
using spirit_test::test_attr;
|
||||
using boost::spirit::x3::expectation_failure;
|
||||
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(expect['x']);
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(char_ > char_);
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test("aa", char_ >> expect[char_])));
|
||||
BOOST_TEST((test("aaa", char_ >> expect[char_ >> char_('a')])));
|
||||
BOOST_TEST((test("xi", char_('x') >> expect[char_('i')])));
|
||||
BOOST_TEST((!test("xi", char_('y') >> expect[char_('o')]))); // should not throw!
|
||||
BOOST_TEST((test("xin", char_('x') >> expect[char_('i') >> char_('n')])));
|
||||
BOOST_TEST((!test("xi", char_('x') >> expect[char_('o')])));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "'o'");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "i");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test("aa", char_ > char_)));
|
||||
BOOST_TEST((test("aaa", char_ > char_ > char_('a'))));
|
||||
BOOST_TEST((test("xi", char_('x') > char_('i'))));
|
||||
BOOST_TEST((!test("xi", char_('y') > char_('o')))); // should not throw!
|
||||
BOOST_TEST((test("xin", char_('x') > char_('i') > char_('n'))));
|
||||
BOOST_TEST((!test("xi", char_('x') > char_('o'))));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "'o'");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "i");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((!test("ay:a", char_ > char_('x') >> ':' > 'a')));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
#ifndef BOOST_SPIRIT_X3_NO_RTTI
|
||||
BOOST_TEST(x.which().find("sequence") != std::string::npos);
|
||||
#else
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "undefined");
|
||||
#endif
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "y:a");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses"
|
||||
#endif
|
||||
{ // Test that attributes with > (sequences) work just like >> (sequences)
|
||||
|
||||
using boost::fusion::vector;
|
||||
using boost::fusion::at_c;
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
BOOST_TEST((test_attr(" a\n b\n c",
|
||||
char_ > char_ > char_, attr, space)));
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
BOOST_TEST((test_attr(" a\n b\n c",
|
||||
char_ > char_ >> char_, attr, space)));
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
BOOST_TEST((test_attr(" a, b, c",
|
||||
char_ >> ',' > char_ >> ',' > char_, attr, space)));
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
std::string attr;
|
||||
BOOST_TEST((test_attr("'azaaz'",
|
||||
"'" > *(char_("a") | char_("z")) > "'", attr, space)));
|
||||
BOOST_TEST(attr == "azaaz");
|
||||
}
|
||||
}
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test(" a a", char_ > char_, space)));
|
||||
BOOST_TEST((test(" x i", char_('x') > char_('i'), space)));
|
||||
BOOST_TEST((!test(" x i", char_('x') > char_('o'), space)));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "'o'");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "i");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test("bar", expect[lit("foo")])));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "\"foo\"");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "bar");
|
||||
}
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
798
test/x3/expect.ipp
Normal file
798
test/x3/expect.ipp
Normal file
@@ -0,0 +1,798 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2013 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 Nana Sakisaka
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
=============================================================================*/
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#include <boost/spirit/home/x3/binary.hpp>
|
||||
#include <boost/spirit/home/x3/directive/with.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/fusion/include/vector.hpp>
|
||||
#include <boost/fusion/include/at.hpp>
|
||||
#include <boost/preprocessor/facilities/overload.hpp>
|
||||
#include <boost/preprocessor/facilities/expand.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test.hpp"
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
# define TEST_SUCCESS_IMPL(tester, ...) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
BOOST_TEST(tester(__VA_ARGS__)); \
|
||||
})
|
||||
|
||||
# define TEST_FAILURE_IMPL_4(tester, input, parser, catch_stmt) \
|
||||
BOOST_TEST_THROWS( \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
BOOST_TEST(tester(input, parser)); \
|
||||
} \
|
||||
catch (expectation_failure<char const*> const& x) \
|
||||
{ \
|
||||
catch_stmt \
|
||||
throw; \
|
||||
} \
|
||||
}, \
|
||||
expectation_failure<char const*> \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_5(tester, input, parser, arg0, catch_stmt) \
|
||||
BOOST_TEST_THROWS( \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
BOOST_TEST(tester(input, parser, arg0)); \
|
||||
} \
|
||||
catch (expectation_failure<char const*> const& x) \
|
||||
{ \
|
||||
catch_stmt \
|
||||
throw; \
|
||||
} \
|
||||
}, \
|
||||
expectation_failure<char const*> \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_6(tester, input, parser, arg0, arg1, catch_stmt) \
|
||||
BOOST_TEST_THROWS( \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
BOOST_TEST(tester(input, parser, arg0, arg1)); \
|
||||
} \
|
||||
catch (expectation_failure<char const*> const& x) \
|
||||
{ \
|
||||
catch_stmt \
|
||||
throw; \
|
||||
} \
|
||||
}, \
|
||||
expectation_failure<char const*> \
|
||||
)
|
||||
|
||||
#else
|
||||
|
||||
using expectation_failure_optional_t = x3::expectation_failure_optional<char const*>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T, typename Enabled = std::enable_if_t<!std::is_const_v<T>>>
|
||||
decltype(auto) wrap_with_expectation_failure(expectation_failure_optional_t&, T& attr)
|
||||
{
|
||||
return attr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto wrap_with_expectation_failure(expectation_failure_optional_t& xopt, T const& skipper)
|
||||
{
|
||||
return x3::with<x3::expectation_failure_tag>(xopt)[skipper];
|
||||
}
|
||||
} // detail
|
||||
|
||||
# define TEST_SUCCESS_IMPL_3(tester, input, parser) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
BOOST_TEST(tester(input, with<expectation_failure_tag>(xopt)[parser])); \
|
||||
BOOST_TEST(!xopt.has_value()); \
|
||||
})
|
||||
|
||||
# define TEST_SUCCESS_IMPL_4(tester, input, parser, arg0) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0) \
|
||||
)); \
|
||||
BOOST_TEST(!xopt.has_value()); \
|
||||
})
|
||||
|
||||
# define TEST_SUCCESS_IMPL_5(tester, input, parser, arg0, arg1) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0), \
|
||||
detail::wrap_with_expectation_failure(xopt, arg1) \
|
||||
)); \
|
||||
BOOST_TEST(!xopt.has_value()); \
|
||||
})
|
||||
|
||||
# define TEST_SUCCESS_IMPL(...) BOOST_PP_EXPAND(BOOST_PP_OVERLOAD(TEST_SUCCESS_IMPL_, __VA_ARGS__) (__VA_ARGS__))
|
||||
|
||||
|
||||
# define TEST_FAILURE_IMPL_4(tester, input, parser, catch_stmt) \
|
||||
BOOST_TEST_NO_THROW( \
|
||||
{ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
do \
|
||||
{ \
|
||||
if (!BOOST_TEST(tester(input, with<expectation_failure_tag>(xopt)[parser]))) break; \
|
||||
if (!BOOST_TEST(xopt.has_value())) break; \
|
||||
auto const& x = *xopt; \
|
||||
catch_stmt \
|
||||
} while (false); \
|
||||
} \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_5(tester, input, parser, arg0, catch_stmt) \
|
||||
BOOST_TEST_NO_THROW( \
|
||||
{ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
do \
|
||||
{ \
|
||||
if (!BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0))) \
|
||||
) break; \
|
||||
\
|
||||
if (!BOOST_TEST(xopt.has_value())) break; \
|
||||
auto const& x = *xopt; \
|
||||
catch_stmt \
|
||||
} while (false); \
|
||||
} \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_6(tester, input, parser, arg0, arg1, catch_stmt) \
|
||||
BOOST_TEST_NO_THROW( \
|
||||
{ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
do \
|
||||
{ \
|
||||
if (!BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0), \
|
||||
detail::wrap_with_expectation_failure(xopt, arg1))) \
|
||||
) break; \
|
||||
\
|
||||
if (!BOOST_TEST(xopt.has_value())) break; \
|
||||
auto const& x = *xopt; \
|
||||
catch_stmt \
|
||||
} while (false); \
|
||||
} \
|
||||
)
|
||||
#endif
|
||||
|
||||
#define TEST_FAILURE_IMPL(...) BOOST_PP_EXPAND(BOOST_PP_OVERLOAD(TEST_FAILURE_IMPL_, __VA_ARGS__) (__VA_ARGS__))
|
||||
|
||||
// Comments below are intentionally written verbosely
|
||||
// to provide human-friendly intellisense tooltip for testers
|
||||
|
||||
// expect_throw: parser = ok, exception = none
|
||||
// expect_nothrow: parser = ok, exception = none, expectation_failure = none
|
||||
#define TEST_SUCCESS_PASS(...) TEST_SUCCESS_IMPL(test, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = ok, exception = none
|
||||
// expect_nothrow: parser = ok, exception = none, expectation_failure = none
|
||||
#define TEST_ATTR_SUCCESS_PASS(...) TEST_SUCCESS_IMPL(test_attr, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = none
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = none
|
||||
#define TEST_SUCCESS_FAIL(...) TEST_SUCCESS_IMPL(!test, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = none
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = none
|
||||
#define TEST_ATTR_SUCCESS_FAIL(...) TEST_SUCCESS_IMPL(!test_attr, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = thrown
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = yes
|
||||
#define TEST_FAILURE(...) TEST_FAILURE_IMPL(!test, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = thrown
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = yes
|
||||
#define TEST_ATTR_FAILURE(...) TEST_FAILURE_IMPL(!test_attr, __VA_ARGS__)
|
||||
|
||||
|
||||
// For testers; development QOL purpose only.
|
||||
|
||||
#define DEBUG_PRINT(x) \
|
||||
do { \
|
||||
std::cout << "----------------------------------\n"; \
|
||||
std::cout << "which: " << x.which() << "\n"; \
|
||||
std::cout << "where: " << x.where() << "\n"; \
|
||||
std::cout << "----------------------------------\n"; \
|
||||
BOOST_TEST(!"remove DEBUG_PRINT before commit!"); \
|
||||
} while (false)
|
||||
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
using x3::standard::alpha;
|
||||
using x3::standard::digit;
|
||||
using x3::standard::space;
|
||||
using x3::standard::blank;
|
||||
using x3::standard::char_;
|
||||
using x3::standard::string;
|
||||
using x3::standard::lit;
|
||||
|
||||
// using x3::lit;
|
||||
using x3::expect;
|
||||
using x3::lexeme;
|
||||
using x3::no_case;
|
||||
using x3::no_skip;
|
||||
using x3::omit;
|
||||
using x3::raw;
|
||||
using x3::skip;
|
||||
using x3::seek;
|
||||
using x3::repeat;
|
||||
using x3::matches;
|
||||
using x3::eps;
|
||||
using x3::eoi;
|
||||
using x3::eol;
|
||||
using x3::attr;
|
||||
using x3::dword;
|
||||
using x3::int_;
|
||||
using x3::symbols;
|
||||
using x3::confix;
|
||||
using x3::with;
|
||||
using x3::expectation_failure;
|
||||
using x3::expectation_failure_tag;
|
||||
|
||||
using boost::fusion::vector;
|
||||
using boost::fusion::at_c;
|
||||
|
||||
using spirit_test::test;
|
||||
using spirit_test::test_attr;
|
||||
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(expect['x']);
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(char_ > char_);
|
||||
|
||||
{
|
||||
TEST_SUCCESS_PASS("aa", char_ >> expect[char_]);
|
||||
TEST_SUCCESS_PASS("aaa", char_ >> expect[char_ >> char_('a')]);
|
||||
TEST_SUCCESS_PASS("xi", char_('x') >> expect[char_('i')]);
|
||||
TEST_SUCCESS_FAIL("xi", char_('y') >> expect[char_('o')]); // should not throw!
|
||||
TEST_SUCCESS_PASS("xin", char_('x') >> expect[char_('i') >> char_('n')]);
|
||||
|
||||
TEST_FAILURE("xi", char_('x') >> expect[char_('o')], {
|
||||
BOOST_TEST(x.which() == "'o'"sv);
|
||||
BOOST_TEST(x.where() == "i"sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
TEST_SUCCESS_PASS("aa", char_ > char_);
|
||||
TEST_SUCCESS_PASS("aaa", char_ > char_ > char_('a'));
|
||||
TEST_SUCCESS_PASS("xi", char_('x') > char_('i'));
|
||||
TEST_SUCCESS_FAIL("xi", char_('y') > char_('o')); // should not throw!
|
||||
TEST_SUCCESS_PASS("xin", char_('x') > char_('i') > char_('n'));
|
||||
|
||||
TEST_FAILURE("xi", char_('x') > char_('o'),
|
||||
{
|
||||
BOOST_TEST(x.which() == "'o'"sv);
|
||||
BOOST_TEST(x.where() == "i"sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
#ifndef BOOST_SPIRIT_X3_NO_RTTI
|
||||
TEST_FAILURE("ay:a", char_ > char_('x') >> ':' > 'a',
|
||||
{
|
||||
BOOST_TEST(x.which().find("sequence") != std::string::npos);
|
||||
BOOST_TEST(x.where() == "y:a"sv);
|
||||
});
|
||||
#else
|
||||
TEST_FAILURE("ay:a", char_ > char_('x') >> ':' > 'a',
|
||||
{
|
||||
BOOST_TEST(x.which() == "undefined"sv);
|
||||
BOOST_TEST(x.where() == "y:a"sv);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses"
|
||||
#endif
|
||||
|
||||
// Test that attributes with > (sequences) work just like >> (sequences)
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
TEST_ATTR_SUCCESS_PASS(" a\n b\n c", char_ > char_ > char_, attr, space);
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
TEST_ATTR_SUCCESS_PASS(" a\n b\n c", char_ > char_ >> char_, attr, space);
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
TEST_ATTR_SUCCESS_PASS(" a, b, c", char_ >> ',' > char_ >> ',' > char_, attr, space);
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
std::string attr;
|
||||
TEST_ATTR_SUCCESS_PASS("'azaaz'", "'" > *(char_("a") | char_("z")) > "'", attr, space);
|
||||
BOOST_TEST(attr == "azaaz");
|
||||
}
|
||||
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
{
|
||||
TEST_SUCCESS_PASS(" a a", char_ > char_, space);
|
||||
TEST_SUCCESS_PASS(" x i", char_('x') > char_('i'), space);
|
||||
|
||||
TEST_FAILURE(" x i", char_('x') > char_('o'), space, {
|
||||
BOOST_TEST(x.which() == "'o'"sv);
|
||||
BOOST_TEST(x.where() == "i"sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
TEST_FAILURE("bar", expect[lit("foo")],
|
||||
{
|
||||
BOOST_TEST(x.which() == "\"foo\""sv);
|
||||
BOOST_TEST(x.where() == "bar"sv);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// skipper
|
||||
{
|
||||
TEST_FAILURE("accb", repeat(7)[alpha], (lit('a') > 'b') | (lit('c') > 'd'), {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "ccb"sv);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// ********* Developers note **********
|
||||
//
|
||||
// As of now (see `git blame`), get_info<T> is still not
|
||||
// specialized for many of the X3 parsers so that the
|
||||
// value of `expectation_failure<...>::which()` will be
|
||||
// implementation-defined demangled string.
|
||||
// Therefore it's essentially impossible to test them
|
||||
// right now; further work must be done.
|
||||
//
|
||||
// Some specific situations are already been reported
|
||||
// (e.g. https://github.com/boostorg/spirit/issues/777)
|
||||
// but we really need to implement all specializations for
|
||||
// X3's predefined parsers, not just the one reported above.
|
||||
//
|
||||
|
||||
|
||||
// sanity check: test expectation_failure propagation
|
||||
// on custom skippers
|
||||
{
|
||||
TEST_SUCCESS_PASS("a..b", lit('a') >> 'b', lit('.') >> '.');
|
||||
TEST_SUCCESS_FAIL("a..b", lit('a') >> 'b', lit('.') >> 'z');
|
||||
|
||||
TEST_SUCCESS_PASS("a b", lit('a') >> 'b', blank);
|
||||
TEST_SUCCESS_PASS("a..b", lit('a') >> 'b', +lit('.'));
|
||||
TEST_SUCCESS_PASS("a..b", lit('a') >> 'b', lit('.') >> '.');
|
||||
|
||||
// if this test fails, there might be a problem in x3::skip_over
|
||||
TEST_FAILURE ("a..b", lit('a') >> 'b', lit('.') >> expect[lit('z')], {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == ".b"sv);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// skip(...) version of the code above
|
||||
// we must test against semantically identical cases!
|
||||
|
||||
TEST_SUCCESS_PASS("a..b", skip(lit('.') >> '.')[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_FAIL("a..b", skip(lit('.') >> 'z')[lit('a') >> 'b']);
|
||||
|
||||
TEST_SUCCESS_PASS("a b", skip(blank)[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_PASS("a..b", skip(+lit('.'))[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_PASS("a..b", skip(lit('.') >> '.')[lit('a') >> 'b']);
|
||||
|
||||
// if this test fails, there might be a problem in x3::skip_over and/or x3::skip_directive
|
||||
TEST_FAILURE ("a..b", skip(lit('.') >> expect[lit('z')])[lit('a') >> 'b'], {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == ".b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// sanity check; test `post_skip` in `x3::phrase_parse(...)`
|
||||
{
|
||||
TEST_SUCCESS_PASS("a..b..", lit('a') >> 'b', lit('.') >> '.');
|
||||
TEST_SUCCESS_FAIL("a..b..", lit('a') >> 'z', lit('.') >> '.');
|
||||
TEST_SUCCESS_FAIL("a..b..", lit('a') >> 'b', lit('.') >> 'z');
|
||||
|
||||
// should fail in `post_skip`
|
||||
TEST_SUCCESS_FAIL("a..b.z", lit('a') >> 'b', lit('.') >> '.');
|
||||
|
||||
// if this test fails, x3::skip_over is BUGGED when `post_skip` is run
|
||||
TEST_FAILURE("a..b.z", lit('a') >> 'b', lit('.') > '.', {
|
||||
BOOST_TEST(x.which() == "'.'"sv);
|
||||
BOOST_TEST(x.where() == "z"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// sequence
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", lit('a') >> 'b');
|
||||
TEST_SUCCESS_FAIL("ac", lit('a') >> 'b');
|
||||
|
||||
TEST_FAILURE("ac", lit('a') >> expect[lit('b')], {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("ac", lit('a') > lit('b'), {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// auxilary parsers
|
||||
{
|
||||
TEST_SUCCESS_PASS("a12", lit('a') > eps > +digit);
|
||||
TEST_SUCCESS_PASS("a12", lit('a') > +digit > eoi);
|
||||
TEST_SUCCESS_PASS("a12\n", lit('a') > +digit > eol);
|
||||
|
||||
TEST_FAILURE("a12", lit('a') > eps(false) > +digit, {
|
||||
BOOST_TEST(x.where() == "12"sv);
|
||||
});
|
||||
TEST_FAILURE("a12", lit('a') > eoi > +digit, {
|
||||
BOOST_TEST(x.where() == "12"sv);
|
||||
});
|
||||
TEST_FAILURE("a12\n", lit('a') > eol > +digit, {
|
||||
BOOST_TEST(x.where() == "12\n"sv);
|
||||
});
|
||||
|
||||
int n = 0;
|
||||
TEST_ATTR_SUCCESS_PASS("abc", lit("abc") > attr(12) > eoi, n);
|
||||
BOOST_TEST(n == 12);
|
||||
}
|
||||
|
||||
// binary, numeric, char, string parsers
|
||||
{
|
||||
TEST_SUCCESS_PASS("12abcd", +digit > dword);
|
||||
TEST_SUCCESS_PASS("abc12", +alpha > int_);
|
||||
TEST_SUCCESS_PASS("12a", +digit > lit('a'));
|
||||
|
||||
TEST_FAILURE("12abc", +digit > dword, {
|
||||
BOOST_TEST(x.where() == "abc"sv);
|
||||
});
|
||||
TEST_FAILURE("abc", +alpha > int_, {
|
||||
BOOST_TEST(x.where() == ""sv);
|
||||
});
|
||||
TEST_FAILURE("12a", +digit > lit('b'), {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "a"sv);
|
||||
});
|
||||
|
||||
symbols<> s;
|
||||
s.add("cat");
|
||||
TEST_SUCCESS_PASS("12cat", +digit > s);
|
||||
TEST_FAILURE("12dog", +digit > s, {
|
||||
BOOST_TEST(x.where() == "dog"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// confix
|
||||
{
|
||||
TEST_SUCCESS_PASS("[12cat]", confix('[', ']')[+digit > lit("cat")]);
|
||||
TEST_FAILURE("[12dog]", confix('[', ']')[+digit > lit("cat")], {
|
||||
BOOST_TEST(x.which() == "\"cat\""sv);
|
||||
BOOST_TEST(x.where() == "dog]"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// expect
|
||||
{
|
||||
TEST_SUCCESS_PASS("abc", lit('a') >> expect[lit('b') >> 'c']);
|
||||
TEST_FAILURE("abc", lit('a') >> expect[lit('b') >> 'd'], {
|
||||
BOOST_TEST(x.where() == "bc"sv);
|
||||
});
|
||||
TEST_FAILURE("abc", lit('a') >> expect[lit('b') > 'd'], {
|
||||
BOOST_TEST(x.which() == "'d'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// lexeme
|
||||
{
|
||||
TEST_SUCCESS_PASS("12 ab", int_ >> lexeme[lit('a') > 'b'], space);
|
||||
TEST_FAILURE("12 a b", int_ >> lexeme[lit('a') > 'b'], space, {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == " b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// matches
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", matches[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_PASS("ac", matches[lit('a') >> 'b'] >> "ac");
|
||||
TEST_SUCCESS_PASS("ab", matches[lit('a') > 'b']);
|
||||
TEST_FAILURE("ac", matches[lit('a') > 'b'] >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
|
||||
bool attr = false;
|
||||
TEST_ATTR_SUCCESS_PASS("ab", matches[lit('a') > 'b'], attr);
|
||||
BOOST_TEST(attr == true);
|
||||
}
|
||||
|
||||
// no_case
|
||||
{
|
||||
TEST_SUCCESS_PASS("12 aB", int_ >> no_case[lit('a') > 'b'], space);
|
||||
TEST_FAILURE("12 aB", int_ >> no_case[lit('a') > 'c'], space, {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "B"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// no_skip
|
||||
{
|
||||
TEST_SUCCESS_PASS("12 3ab", int_ >> int_ >> no_skip[lit('a') > 'b'], space);
|
||||
TEST_FAILURE("12 3ab", int_ >> int_ >> no_skip[lit('a') > 'c'], space, {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// skip
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab[]c[]d", skip(lit('[') > ']')[+alpha]);
|
||||
TEST_FAILURE("ab[]c[5]d", skip(lit('[') > ']')[+alpha], {
|
||||
BOOST_TEST(x.which() == "']'"sv);
|
||||
BOOST_TEST(x.where() == "5]d"sv);
|
||||
});
|
||||
|
||||
TEST_SUCCESS_PASS("a1[]b2c3[]d4", skip(lit('[') > ']')[+(alpha > digit)]);
|
||||
TEST_FAILURE("a1[]b2c3[]d", skip(lit('[') > ']')[+(alpha > digit)], {
|
||||
BOOST_TEST(x.where() == ""sv);
|
||||
});
|
||||
|
||||
TEST_FAILURE("a b c", lit('a') > 'c', space, {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b c"sv);
|
||||
});
|
||||
|
||||
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d", skip(space)[*char_ > lit('z')], s, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == ""sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_SUCCESS_PASS("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('c') > char_('d')], s, blank);
|
||||
BOOST_TEST(s == "abcd");
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('z') > skip(space)[char_('c') > char_('d')], s, blank, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == "b\n c\n d"sv);
|
||||
});
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('z') > char_('d')], s, blank, {
|
||||
BOOST_TEST(x.where() == "\n c\n d"sv);
|
||||
});
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('c') > char_('z')], s, blank, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == "d"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// reskip
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_SUCCESS_PASS("a b c d", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('d')]], s, blank);
|
||||
BOOST_TEST(s == "abcd");
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('z')]], s, blank, {
|
||||
BOOST_TEST(x.where() == "d"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// reskip with expectation failure context propagation
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_SUCCESS_PASS("a b c d e", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('d') > char_('e')]], s, blank);
|
||||
BOOST_TEST(s == "abcde");
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d e", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('z') > char_('e')]], s, blank, {
|
||||
BOOST_TEST(x.where() == " d e"sv);
|
||||
});
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d e", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('d') > char_('z')]], s, blank, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == "e"sv);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// omit
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", omit[lit('a') > 'b']);
|
||||
TEST_FAILURE("ab", omit[lit('a') > 'c'], {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// raw
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", raw[lit('a') > 'b']);
|
||||
TEST_FAILURE("ab", raw[lit('a') > 'c'], {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// repeat
|
||||
{
|
||||
TEST_SUCCESS_PASS("ababac", repeat(1, 3)[lit('a') >> 'b'] >> "ac" | +alpha);
|
||||
|
||||
TEST_FAILURE("ababac", repeat(1, 3)[lit('a') > 'b'] | +alpha, {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("acab", repeat(2, 3)[lit('a') > 'b'] | +alpha, {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "cab"sv);
|
||||
});
|
||||
|
||||
TEST_SUCCESS_PASS("bcab", repeat(2, 3)[lit('a') > 'b'] | +alpha);
|
||||
}
|
||||
|
||||
// seek
|
||||
{
|
||||
TEST_SUCCESS_PASS("a1b1c1", seek[lit('c') > '1']);
|
||||
TEST_FAILURE("a1b1c2c1", seek[lit('c') > '1'], {
|
||||
BOOST_TEST(x.which() == "'1'"sv);
|
||||
BOOST_TEST(x.where() == "2c1"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// alternative
|
||||
{
|
||||
TEST_SUCCESS_PASS("ac", lit('a') >> 'b' | "ac");
|
||||
TEST_SUCCESS_PASS("ac", lit('a') >> 'b' | lit('a') >> 'd' | "ac");
|
||||
|
||||
TEST_FAILURE("ac", (lit('a') > 'b') | "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("ac", lit('a') >> 'b' | (lit('a') > 'd') | "ac", {
|
||||
BOOST_TEST(x.which() == "'d'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// predicate
|
||||
{
|
||||
TEST_SUCCESS_PASS("abc", lit('a') >> &(lit('b') > 'c') >> "bc");
|
||||
TEST_FAILURE("abc", lit('a') >> &(lit('b') > 'd') >> "bc", {
|
||||
BOOST_TEST(x.which() == "'d'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// difference
|
||||
{
|
||||
TEST_SUCCESS_PASS("bcac", *(char_ - (lit('a') >> 'b')));
|
||||
TEST_SUCCESS_PASS("bcab", *(char_ - (lit('a') > 'b')) >> "ab");
|
||||
TEST_FAILURE("bcac", *(char_ - (lit('a') > 'b')) >> "ab", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// kleene
|
||||
{
|
||||
TEST_SUCCESS_PASS("abac", *(lit('a') >> 'b') >> "ac");
|
||||
TEST_SUCCESS_PASS("abbc", *(lit('a') > 'b') >> "bc");
|
||||
TEST_FAILURE("abac", *(lit('a') > 'b') >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// list
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab::ab::ac", (lit('a') >> 'b') % (lit(':') >> ':') >> "::ac");
|
||||
TEST_SUCCESS_PASS("ab::ab:ac", (lit('a') > 'b') % (lit(':') >> ':') >> ":ac");
|
||||
|
||||
TEST_FAILURE("ab::ab::ac", (lit('a') > 'b') % (lit(':') >> ':') >> "::ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("ab::ab:ab", (lit('a') >> 'b') % (lit(':') > ':') >> ":ab", {
|
||||
BOOST_TEST(x.which() == "':'"sv);
|
||||
BOOST_TEST(x.where() == "ab"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// not
|
||||
{
|
||||
TEST_SUCCESS_PASS("[ac]", lit('[') >> !(lit('a') >> 'b') >> +alpha >> ']');
|
||||
TEST_SUCCESS_PASS("[bc]", lit('[') >> !(lit('a') > 'b') >> +alpha >> ']');
|
||||
TEST_FAILURE("[ac]", lit('[') >> !(lit('a') > 'b') >> +alpha >> ']', {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c]"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// optional
|
||||
{
|
||||
TEST_SUCCESS_PASS("ac", -(lit('a') >> 'b') >> "ac");
|
||||
TEST_SUCCESS_PASS("ab", -(lit('a') > 'b'));
|
||||
TEST_FAILURE("ac", -(lit('a') > 'b') >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// plus
|
||||
{
|
||||
TEST_SUCCESS_PASS("abac", +(lit('a') >> 'b') >> "ac");
|
||||
TEST_SUCCESS_PASS("abbc", +(lit('a') > 'b') >> "bc");
|
||||
TEST_FAILURE("abac", +(lit('a') > 'b') >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
8
test/x3/expect_nothrow.cpp
Normal file
8
test/x3/expect_nothrow.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 Nana Sakisaka
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
=============================================================================*/
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 0
|
||||
#include "expect.ipp"
|
||||
8
test/x3/expect_throw.cpp
Normal file
8
test/x3/expect_throw.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 Nana Sakisaka
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
=============================================================================*/
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 1
|
||||
#include "expect.ipp"
|
||||
Reference in New Issue
Block a user