Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support nothrow expect #242

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/x3/quick_reference.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ pages and pages of reference documentation.
[[__x3_sequence__] [`tuple<A, B>`] [Sequence. Parse `a` followed by `b`]]
[[__x3_expect__] [`tuple<A, B>`] [Expect. Parse `a` followed by `b`. `b` is
expected to match when `a` matches, otherwise,
an `expectation_failure` is thrown.]]
the whole parser fails. The failure can be
handled by error handler, such as on_error().]]
[[__x3_difference__] [`A`] [Difference. Parse `a` but not `b`]]
[[__x3_list__] [`vector<A>`] [List. Parse `a` delimited `b` one or more times]]
]
Expand Down
20 changes: 11 additions & 9 deletions include/boost/spirit/home/x3/auxiliary/guard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,30 @@ namespace boost { namespace spirit { namespace x3
{
for (;;)
{
try
Iterator i = first;
if (this->subject.parse(i, last, context, rcontext, attr))
{
Iterator i = first;
bool r = this->subject.parse(i, last, context, rcontext, attr);
if (r)
first = i;
return r;
first = i;
return true;
}
catch (expectation_failure<Iterator> const& x)
else if (has_expectation_failure(context))
{
switch (handler(first, last, x, context))
auto const& failure =
get_expectation_failure(first, context);
switch (handler(first, last, failure, context))
{
case error_handler_result::fail:
reset_expectation_failure(context);
return false;
case error_handler_result::retry:
continue;
case error_handler_result::accept:
return true;
case error_handler_result::rethrow:
throw;
return false;
}
}
return false;
}
return false;
}
Expand Down
19 changes: 15 additions & 4 deletions include/boost/spirit/home/x3/core/parse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ namespace boost { namespace spirit { namespace x3
// If you get an error no matching function for call to 'as_parser'
// here, then p is not a parser or there is no suitable conversion
// from p to a parser.
return as_parser(p).parse(first, last, unused, unused, attr);
boost::optional<expectation_failure<Iterator>> failure;
return as_parser(p).parse(first, last,
make_context<expectation_failure_tag>(failure), unused, attr);
}

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -114,7 +116,10 @@ namespace boost { namespace spirit { namespace x3
// here, for either p or s, then p or s is not a parser or there is
// no suitable conversion from p to a parser.
auto skipper_ctx = make_context<skipper_tag>(as_parser(s));
bool r = as_parser(p).parse(first, last, skipper_ctx, unused, attr);
boost::optional<expectation_failure<Iterator>> failure;
bool r = as_parser(p).parse(first, last,
make_context<expectation_failure_tag>(failure, skipper_ctx),
unused, attr);
if (post_skip == skip_flag::post_skip)
x3::skip_over(first, last, skipper_ctx);
return r;
Expand Down Expand Up @@ -177,11 +182,17 @@ namespace boost { namespace spirit { namespace x3
}

///////////////////////////////////////////////////////////////////////////
template <typename Skipper>
template <typename Iterator, typename Skipper>
struct phrase_parse_context
{
typedef decltype(
make_context<skipper_tag>(as_parser(std::declval<Skipper>())))
make_context<expectation_failure_tag>(
std::declval<
boost::optional<expectation_failure<Iterator>>&
>(),
make_context<skipper_tag>(as_parser(std::declval<Skipper>()))
)
)
type;
};
}}}
Expand Down
3 changes: 2 additions & 1 deletion include/boost/spirit/home/x3/core/proxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
#include <boost/spirit/home/x3/support/traits/attribute_category.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>

namespace boost { namespace spirit { namespace x3
{
Expand All @@ -29,7 +30,7 @@ namespace boost { namespace spirit { namespace x3
, Context const& context, RuleContext& rcontext, Attribute& attr, Category) const
{
this->subject.parse(first, last, context, rcontext, attr);
return true;
return !has_expectation_failure(context);
}

// Main entry point.
Expand Down
24 changes: 23 additions & 1 deletion include/boost/spirit/home/x3/core/skip_over.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/declval.hpp>
#include <boost/optional.hpp>

namespace boost { namespace spirit { namespace x3
{
Expand All @@ -31,6 +32,26 @@ namespace boost { namespace spirit { namespace x3
Skipper const& skipper;
};

struct expectation_failure_tag;

template <typename Iterator>
struct expectation_failure
{
public:

expectation_failure(Iterator where, std::string which)
: where_(where), which_(std::move(which))
{}

std::string which() const { return which_; }
Iterator const& where() const { return where_; }

private:

Iterator where_;
std::string which_;
};

namespace detail
{
template <typename Skipper>
Expand Down Expand Up @@ -62,7 +83,8 @@ namespace boost { namespace spirit { namespace x3
inline void skip_over(
Iterator& first, Iterator const& last, Skipper const& skipper)
{
while (first != last && skipper.parse(first, last, unused, unused, unused))
boost::optional<expectation_failure<Iterator>> failure;
while (first != last && skipper.parse(first, last, make_context<expectation_failure_tag>(failure), unused, unused))
/***/;
}

Expand Down
125 changes: 101 additions & 24 deletions include/boost/spirit/home/x3/directive/expect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,101 @@
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>

#include <boost/throw_exception.hpp>
#include <stdexcept>

namespace boost { namespace spirit { namespace x3
{
template <typename Iterator>
struct expectation_failure : std::runtime_error
{
public:
namespace detail {
inline bool has_expectation_failure_impl(bool& failure) {
return failure;
}

expectation_failure(Iterator where, std::string const& which)
: std::runtime_error("boost::spirit::x3::expectation_failure")
, where_(where), which_(which)
{}
~expectation_failure() throw() {}
template <typename Iterator>
bool has_expectation_failure_impl(
boost::optional<expectation_failure<Iterator>>& failure
) {
return static_cast<bool>(failure);
}

std::string which() const { return which_; }
Iterator const& where() const { return where_; }
template <typename Iterator, typename Subject>
void set_expectation_failure_impl(
Iterator const& where,
Subject const& subject,
bool& failure
) {
failure = true;
}

private:
template <typename Iterator, typename Subject>
void set_expectation_failure_impl(
Iterator const& where,
Subject const& subject,
boost::optional<expectation_failure<Iterator>>& failure
) {
failure = expectation_failure<Iterator>{
where,
what(subject)
};
}

Iterator where_;
std::string which_;
};
template <typename Iterator>
expectation_failure<Iterator> get_expectation_failure_impl(
Iterator const& where, bool& failure
) {
return {where, {}};
}

template <typename Iterator>
auto const& get_expectation_failure_impl(
Iterator const&,
boost::optional<expectation_failure<Iterator>>& failure
) {
return *failure;
}

template <typename Iterator>
void reset_expectation_failure_impl(bool& failure) {
failure = false;
}

template <typename Iterator>
void reset_expectation_failure_impl(
boost::optional<expectation_failure<Iterator>>& failure) {
failure = boost::none;
}
}

template <typename Context>
bool has_expectation_failure(Context const& context) {
return detail::has_expectation_failure_impl(
x3::get<expectation_failure_tag>(context));
}

template <typename Iterator, typename Subject, typename Context>
void set_expectation_failure(
Iterator const& where,
Subject const& subject,
Context const& context
) {
detail::set_expectation_failure_impl(
where,
subject,
x3::get<expectation_failure_tag>(context)
);
}

template <typename Iterator, typename Context>
decltype(auto) get_expectation_failure(
Iterator const& where,
Context const& context
) {
return detail::get_expectation_failure_impl(
where, x3::get<expectation_failure_tag>(context));
}

template <typename Context>
void reset_expectation_failure(Context const& context) {
detail::reset_expectation_failure_impl(
x3::get<expectation_failure_tag>(context));
}

template <typename Subject>
struct expect_directive : unary_parser<Subject, expect_directive<Subject>>
Expand All @@ -50,13 +121,16 @@ namespace boost { namespace spirit { namespace x3
bool parse(Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr) const
{
Iterator save = first;
bool r = this->subject.parse(first, last, context, rcontext, attr);

if (!r)
{
boost::throw_exception(
expectation_failure<Iterator>(
first, what(this->subject)));
if(!has_expectation_failure(context))
{
set_expectation_failure(first, this->subject, context);
}
first = save;
}
return r;
}
Expand Down Expand Up @@ -87,14 +161,17 @@ namespace boost { namespace spirit { namespace x3 { namespace detail
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr)
{
Iterator save = first;
bool r = parse_into_container(
parser.subject, first, last, context, rcontext, attr);

if (!r)
{
boost::throw_exception(
expectation_failure<Iterator>(
first, what(parser.subject)));
if (!has_expectation_failure(context))
{
set_expectation_failure(first, parser.subject, context);
}
first = save;
}
return r;
}
Expand Down
3 changes: 3 additions & 0 deletions include/boost/spirit/home/x3/directive/matches.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>

namespace boost { namespace spirit { namespace x3
{
Expand All @@ -30,6 +31,8 @@ namespace boost { namespace spirit { namespace x3
{
bool const result = this->subject.parse(
first, last, context, rcontext, unused);
if (has_expectation_failure(context))
return false;
traits::move_to(result, attr);
return true;
}
Expand Down
3 changes: 2 additions & 1 deletion include/boost/spirit/home/x3/directive/repeat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <boost/function_types/parameter_types.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/operator/kleene.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>

namespace boost { namespace spirit { namespace x3 { namespace detail
{
Expand Down Expand Up @@ -85,7 +86,7 @@ namespace boost { namespace spirit { namespace x3
this->subject, first, last, context, rcontext, attr))
break;
}
return true;
return !has_expectation_failure(context);
}

RepeatCountLimit repeat_limit;
Expand Down
5 changes: 5 additions & 0 deletions include/boost/spirit/home/x3/directive/seek.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define BOOST_SPIRIT_X3_SEEK_APRIL_13_2014_1920PM

#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>

namespace boost { namespace spirit { namespace x3
{
Expand Down Expand Up @@ -36,6 +37,10 @@ namespace boost { namespace spirit { namespace x3
first = current;
return true;
}
else if (has_expectation_failure(context))
{
return false;
}
}

// Test for when subjects match on input empty. Example:
Expand Down
Loading