2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00

Revert "Define the concept of "parser" in X3 (#807)"

This reverts commit d21af8b48d.
This commit is contained in:
Nana Sakisaka
2025-09-13 08:57:11 +09:00
parent ec681a2484
commit d88f114559
13 changed files with 461 additions and 1118 deletions

View File

@@ -17,30 +17,30 @@ jobs:
fail-fast: false
matrix:
include:
- name: "C++23 test/x3"
- name: "C++17 test/x3"
buildtype: "boost"
packages: "clang++-20 libc++-20-dev libc++abi-20-dev libunwind-20-dev jq ccache"
packages: "clang-18 libc++-18-dev libc++abi-18-dev libunwind-18-dev jq ccache"
packages_to_remove: ""
os: "ubuntu-24.04"
cxx: "clang++-20"
os: "ubuntu-22.04"
cxx: "clang-18"
sources: ""
llvm_os: "noble"
llvm_ver: "20"
std: "23"
llvm_os: "jammy"
llvm_ver: "18"
std: "17"
job: "test/x3"
travis_compiler: "clang-20"
- name: "C++23 test/x3"
travis_compiler: "clang-18"
- name: "C++17 test/x3"
buildtype: "boost"
packages: "g++-14 jq ccache"
packages: "g++-13 jq ccache"
packages_to_remove: ""
os: "ubuntu-24.04"
cxx: "g++-14"
os: "ubuntu-22.04"
cxx: "gcc-13"
sources: ""
llvm_os: ""
llvm_ver: ""
std: "23"
std: "17"
job: "test/x3"
travis_compiler: "gcc-14"
travis_compiler: "gcc-13"
- name: "C++11 test/qi"
buildtype: "boost"
packages: "clang-14 libc++-14-dev libc++abi-14-dev libunwind-14-dev jq ccache"
@@ -219,7 +219,7 @@ jobs:
done
fi
echo ">>>>> APT: INSTALL ${PACKAGES}.."
sudo -E DEBIAN_FRONTEND=noninteractive apt-get -o Acquire::Retries=3 -y install ${PACKAGES}
sudo -E DEBIAN_FRONTEND=noninteractive apt-get -o Acquire::Retries=3 -y --no-install-suggests --no-install-recommends install ${PACKAGES}
echo '==================================> INSTALL AND COMPILE'
set -e

View File

@@ -1,7 +1,6 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2013-2014 Agustin Berge
Copyright (c) 2025 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)
@@ -10,184 +9,142 @@
#define BOOST_SPIRIT_X3_AUXILIARY_ANY_PARSER_APR_09_2014_1145PM
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/subcontext.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
#include <boost/spirit/home/x3/support/traits/has_attribute.hpp>
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
#include <boost/assert.hpp>
#include <iterator>
#include <boost/spirit/home/x3/support/traits/is_parser.hpp>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace boost::spirit::x3
namespace boost { namespace spirit { namespace x3
{
// Type-erased parser that holds any underlying parser which models `X3Parser<It, It>`.
template <
std::forward_iterator It, // used only for polymorphism
typename Attribute = unused_type,
typename Context = unused_type // used only for polymorphism
>
struct any_parser : parser<any_parser<It, Attribute, Context>>
typename Iterator
, typename Attribute = unused_type
, typename Context = subcontext<>>
struct any_parser : parser<any_parser<Iterator, Attribute, Context>>
{
// `any_parser` existed historically because writing a type-erased parser in C++
// was cumbersome before modern language features. Its use is now discouraged for
// technical reasons:
//
// 1) Performance: type erasure places dynamic indirection on the parsing hot path.
// Every token step must operate through type-erased iterators/contexts,
// inhibiting inlining and adding dispatch overhead.
//
// 2) Formal grounds: PEGs are deterministic (ordered choice) and do not require
// runtime reification of the grammar. The same behaviors can be expressed as
// a static composition of parsers with semantic predicates.
//
// 3) Empirical evidence: a GitHub full-text search indicates that `any_parser`
// is rarely used in practice.
//
// Reaching for `any_parser` means attempting to *define* the right-hand side of the
// grammar dynamically from the left-hand side of the input. That indirection is
// unnecessary in PEG; you can always structure the grammar statically. If more
// complex branching is required, use the symbols parser or design a fully featured
// parser class for your specific grammar.
//
// Note for X3 developers: if we allow the existence of `any_parser`, it would become
// a very undesirable technical burden which prohibits the future enhancements.
// Imagine that we're implementing an out-of-the-box packrat memoization support in
// the future. The existence of `any_parser` makes the situation undesirably complex,
// as we need to consider the *dynamic* properties of stateful parsers.
typedef Attribute attribute_type;
using attribute_type = Attribute;
static constexpr bool has_attribute = !std::is_same_v<Attribute, unused_type>;
static constexpr bool handles_container = traits::is_container<Attribute>::value;
static bool const has_attribute =
!is_same<unused_type, attribute_type>::value;
static bool const handles_container =
traits::is_container<Attribute>::value;
public:
constexpr any_parser() = default;
any_parser() = default;
template <typename Parser>
requires
(!std::is_same_v<std::remove_cvref_t<Parser>, any_parser>) &&
X3Parser<Parser, It, It>
[[deprecated(
"`any_parser` is deprecated. Prefer static grammar "
"(e.g. `a_prefix >> a_body | b_prefix >> b_body`) or "
"static branching (e.g. `x3::eps[lazy_cond_func] >> a | b`)."
)]]
constexpr any_parser(Parser&& parser)
: parser_(std::make_unique<holder<as_parser_plain_t<Parser>>>(
std::forward<Parser>(parser)
))
{}
template <typename Expr,
typename Enable = typename enable_if<traits::is_parser<Expr>>::type>
any_parser(Expr const& expr)
: _content(new holder<Expr>(expr)) {}
constexpr any_parser(any_parser const& other)
: parser_(other.parser_ ? other.parser_->clone() : nullptr)
{}
any_parser(any_parser const& other)
: _content(other._content ? other._content->clone() : nullptr) {}
constexpr any_parser(any_parser&& other) = default;
any_parser(any_parser&& other) = default;
constexpr any_parser& operator=(any_parser const& other)
any_parser& operator=(any_parser const& other)
{
parser_.reset(other.parser_ ? other.parser_->clone() : nullptr);
_content.reset(other._content ? other._content->clone() : nullptr);
return *this;
}
any_parser& operator=(any_parser&& other) = default;
// Runtime attribute is the same type as the predefined attribute type
template <std::forward_iterator It_, std::sentinel_for<It_> Se_, typename Context_>
[[nodiscard]] constexpr bool
parse(
It_& first, Se_ const& last,
Context_ const& context, unused_type, Attribute& attr
) const
template <typename Iterator_, typename Context_>
bool parse(Iterator_& first, Iterator_ const& last
, Context_ const& context, unused_type, Attribute& attr) const
{
static_assert(std::is_same_v<It_, It>, "Runtime iterator type must match `any_parser`'s iterator type");
BOOST_ASSERT_MSG(parser_ != nullptr, "Invalid use of uninitialized any_parser");
BOOST_STATIC_ASSERT_MSG(
(is_same<Iterator, Iterator_>::value)
, "Incompatible iterator used"
);
return parser_->parse(first, last, context, attr);
BOOST_ASSERT_MSG(
(_content != nullptr)
, "Invalid use of uninitialized any_parser"
);
return _content->parse(first, last, context, attr);
}
// Runtime attribute is NOT the same type as the predefined attribute type
template <std::forward_iterator It_, std::sentinel_for<It_> Se_, typename Context_, typename Attribute_>
[[nodiscard]] constexpr bool
parse(
It_& first, Se_ const& last,
Context_ const& context, unused_type, Attribute_& attr_
) const
template <typename Iterator_, typename Context_, typename Attribute_>
bool parse(Iterator_& first, Iterator_ const& last
, Context_ const& context, unused_type, Attribute_& attr_) const
{
Attribute attr; // default-initialize
if (this->parse(first, last, context, unused, attr))
Attribute attr;
if (parse(first, last, context, unused, attr))
{
traits::move_to(std::move(attr), attr_);
traits::move_to(attr, attr_);
return true;
}
return false;
}
[[nodiscard]] constexpr std::string get_x3_info() const
std::string get_info() const
{
return parser_ ? parser_->get_x3_info() : "(uninitialized `any_parser`)";
return _content ? _content->get_info() : "";
}
private:
struct placeholder
{
constexpr virtual ~placeholder() = default;
virtual placeholder* clone() const = 0;
[[nodiscard]]
constexpr virtual std::unique_ptr<placeholder> clone() const = 0;
virtual bool parse(Iterator& first, Iterator const& last
, Context const& context, Attribute& attr) const = 0;
[[nodiscard]]
constexpr virtual bool parse(
It& first, It const& last, Context const& context, Attribute& attr
) const = 0;
virtual std::string get_info() const = 0;
[[nodiscard]]
constexpr virtual std::string get_x3_info() const = 0;
virtual ~placeholder() {}
};
template <X3Parser<It, It> Parser>
template <typename Expr>
struct holder : placeholder
{
template <X3Subject Subject>
requires is_parser_constructible_v<Parser, Subject>
constexpr explicit holder(Subject&& subject)
noexcept(is_parser_nothrow_constructible_v<Parser, Subject>)
: parser_(as_parser(std::forward<Subject>(subject)))
{}
typedef typename extension::as_parser<Expr>::value_type parser_type;
[[nodiscard]]
constexpr std::unique_ptr<placeholder> clone() const override
explicit holder(Expr const& p)
: _parser(as_parser(p)) {}
holder* clone() const override
{
return std::make_unique<holder>(*this);
return new holder(*this);
}
[[nodiscard]]
constexpr bool parse(
It& first, It const& last,
Context const& context, Attribute& attr
) const override
bool parse(Iterator& first, Iterator const& last
, Context const& context, Attribute& attr) const override
{
static_assert(Parsable<Parser, It, It, Context, unused_type, Attribute>);
return parser_.parse(first, last, context, unused, attr);
return _parser.parse(first, last, context, unused, attr);
}
[[nodiscard]]
constexpr std::string get_x3_info() const override
std::string get_info() const override
{
return x3::what(parser_);
return x3::what(_parser);
}
private:
Parser parser_;
parser_type _parser;
};
std::unique_ptr<placeholder> parser_;
private:
std::unique_ptr<placeholder> _content;
};
} // boost::spirit::x3
template <typename Iterator, typename Attribute, typename Context>
struct get_info<any_parser<Iterator, Attribute, Context>>
{
typedef std::string result_type;
std::string operator()(
any_parser<Iterator, Attribute, Context> const& p) const
{
return p.get_info();
}
};
}}}
#endif

View File

@@ -1,6 +1,5 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2025 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)
@@ -9,173 +8,80 @@
#define BOOST_SPIRIT_X3_PARSE_APRIL_16_2006_0442PM
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/expectation.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/parse_result.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/iterator/iterator_concepts.hpp>
#include <optional>
#include <iterator>
#include <ranges>
#include <string_view>
#include <type_traits>
#include <utility>
namespace boost::spirit::x3
namespace boost { namespace spirit { namespace x3
{
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, typename Attribute>
[[nodiscard]] constexpr bool
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser, typename Attribute>
inline bool
parse_main(
It& first,
Se last,
Parser&& p,
Attribute& attr
)
Iterator& first
, Iterator last
, Parser const& p
, Attribute& attr)
{
static_assert(Parsable<Parser, It, Se, unused_type, unused_type, Attribute>);
return as_parser(std::forward<Parser>(p)).parse(first, last, unused, unused, attr);
}
// Make sure the iterator is at least a readable forward traversal iterator.
// If you got a compilation error here, then you are using a weaker iterator
// while calling this function, you need to supply a readable forward traversal
// iterator instead.
BOOST_CONCEPT_ASSERT((boost_concepts::ReadableIteratorConcept<Iterator>));
BOOST_CONCEPT_ASSERT((boost_concepts::ForwardTraversalConcept<Iterator>));
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, typename Attribute>
[[nodiscard]] constexpr bool
parse(
It& first,
Se last,
Parser&& p,
Attribute& attr
)
{
return x3::parse_main(first, last, std::forward<Parser>(p), attr);
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, typename Attribute>
[[nodiscard]] constexpr bool
parse(
It const& first_,
Se last,
Parser&& p,
Attribute& attr
)
{
It first = first_;
return x3::parse_main(first, last, std::forward<Parser>(p), attr);
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser>
[[nodiscard]] constexpr bool
parse(
It& first,
Se last,
Parser&& p
)
{
return x3::parse_main(first, last, std::forward<Parser>(p), unused);
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser>
[[nodiscard]] constexpr bool
parse(
It const& first_,
Se last,
Parser&& p
)
{
It first = first_;
return x3::parse_main(first, last, std::forward<Parser>(p), unused);
// 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);
}
///////////////////////////////////////////////////////////////////////////
template <std::ranges::forward_range R, X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser, typename Attribute>
[[nodiscard]] constexpr parse_result_for<R>
parse_main(
R& range,
Parser&& p,
Attribute& attr
)
{
using It = std::ranges::iterator_t<R>;
using Se = std::ranges::sentinel_t<R>;
std::optional<x3::expectation_failure<It>> expect_failure;
auto failure_ctx = x3::make_context<expectation_failure_tag>(expect_failure);
using Context = decltype(failure_ctx);
static_assert(Parsable<Parser, It, Se, Context, unused_type, Attribute>);
It first = std::ranges::begin(range);
Se last = std::ranges::end(range);
bool const ok = as_parser(std::forward<Parser>(p)).parse(first, last, failure_ctx, unused, attr);
return parse_result_for<R>{
.ok = ok,
.expect_failure = std::move(expect_failure),
.remainder = {std::move(first), std::move(last)}
};
}
template <std::ranges::forward_range R, X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser, typename Attribute>
constexpr void
parse_main(
parse_result_for<R>& res
, R& range
, Parser&& p
template <typename Iterator, typename Parser, typename Attribute>
inline bool
parse(
Iterator& first
, Iterator last
, Parser const& p
, Attribute& attr)
{
using It = std::ranges::iterator_t<R>;
using Se = std::ranges::sentinel_t<R>;
res.expect_failure.reset();
auto failure_ctx = x3::make_context<expectation_failure_tag>(res.expect_failure);
using Context = decltype(failure_ctx);
static_assert(Parsable<Parser, It, Se, Context, unused_type, Attribute>);
It first = std::ranges::begin(range);
Se last = std::ranges::end(range);
res.ok = as_parser(std::forward<Parser>(p)).parse(first, last, failure_ctx, unused, attr);
res.remainder = {std::move(first), std::move(last)};
return parse_main(first, last, p, attr);
}
template <std::ranges::forward_range R, X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser, typename Attribute>
[[nodiscard]] constexpr parse_result_for<R>
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser, typename Attribute>
inline bool
parse(
R& range
, Parser&& p
Iterator const& first_
, Iterator last
, Parser const& p
, Attribute& attr)
{
return x3::parse_main(range, std::forward<Parser>(p), attr);
Iterator first = first_;
return parse_main(first, last, p, attr);
}
template <std::ranges::forward_range R, X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser>
[[nodiscard]] constexpr parse_result_for<R>
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser>
inline bool
parse(
R& range
, Parser&& p)
Iterator& first
, Iterator last
, Parser const& p)
{
return x3::parse_main(range, std::forward<Parser>(p), unused);
return parse_main(first, last, p, unused);
}
template <std::ranges::forward_range R, X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser, typename Attribute>
constexpr void
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser>
inline bool
parse(
parse_result_for<R>& res,
R& range,
Parser&& p,
Attribute& attr
)
Iterator const& first_
, Iterator last
, Parser const& p)
{
return x3::parse_main(res, range, std::forward<Parser>(p), attr);
}
template <std::ranges::forward_range R, X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser>
constexpr void
parse(
parse_result_for<R>& res,
R& range,
Parser&& p
)
{
return x3::parse_main(res, range, std::forward<Parser>(p), unused);
Iterator first = first_;
return parse_main(first, last, p, unused);
}
///////////////////////////////////////////////////////////////////////////
@@ -185,306 +91,101 @@ namespace boost::spirit::x3
dont_post_skip // inhibit post-skipping in phrase_parse()
};
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, X3Parser<It, Se> Skipper, typename Attribute>
[[nodiscard]] constexpr bool
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser, typename Skipper, typename Attribute>
inline bool
phrase_parse_main(
It& first,
Se last,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
Iterator& first
, Iterator last
, Parser const& p
, Skipper const& s
, Attribute& attr
, skip_flag post_skip = skip_flag::post_skip)
{
auto skipper_ctx = x3::make_context<skipper_tag>(as_parser(std::forward<Skipper>(s)));
using Context = decltype(skipper_ctx);
static_assert(Parsable<Parser, It, Se, Context, unused_type, Attribute>);
// Make sure the iterator is at least a readable forward traversal iterator.
// If you got a compilation error here, then you are using a weaker iterator
// while calling this function, you need to supply a readable forward traversal
// iterator instead.
BOOST_CONCEPT_ASSERT((boost_concepts::ReadableIteratorConcept<Iterator>));
BOOST_CONCEPT_ASSERT((boost_concepts::ForwardTraversalConcept<Iterator>));
static_assert(!std::is_same<Skipper, unused_type>::value,
"Error! Skipper cannot be unused_type.");
bool const ok = as_parser(std::forward<Parser>(p)).parse(first, last, skipper_ctx, unused, attr);
// If you get an error no matching function for call to 'as_parser'
// 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);
if (post_skip == skip_flag::post_skip)
{
x3::skip_over(first, last, skipper_ctx);
}
return ok;
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, X3Parser<It, Se> Skipper, typename Attribute>
[[nodiscard]] constexpr bool
phrase_parse(
It& first,
Se last,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
{
return x3::phrase_parse_main(first, last, std::forward<Parser>(p), std::forward<Skipper>(s), attr, post_skip);
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, X3Parser<It, Se> Skipper, typename Attribute>
[[nodiscard]] constexpr bool
phrase_parse(
It const& first_,
Se last,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
{
It first = first_;
return x3::phrase_parse_main(first, last, std::forward<Parser>(p), std::forward<Skipper>(s), attr, post_skip);
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, X3Parser<It, Se> Skipper>
[[nodiscard]] constexpr bool
phrase_parse(
It& first,
Se last,
Parser&& p,
Skipper&& s,
skip_flag post_skip = skip_flag::post_skip
)
{
return x3::phrase_parse_main(first, last, std::forward<Parser>(p), std::forward<Skipper>(s), unused, post_skip);
}
template <std::forward_iterator It, std::sentinel_for<It> Se, X3Parser<It, Se> Parser, X3Parser<It, Se> Skipper>
[[nodiscard]] constexpr bool
phrase_parse(
It const& first_,
Se last,
Parser&& p,
Skipper&& s,
skip_flag post_skip = skip_flag::post_skip
)
{
It first = first_;
return x3::phrase_parse_main(first, last, std::forward<Parser>(p), std::forward<Skipper>(s), unused, post_skip);
return r;
}
///////////////////////////////////////////////////////////////////////////
template <
std::ranges::forward_range R,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Skipper,
typename Attribute
>
[[nodiscard]] constexpr parse_result_for<R>
phrase_parse_main(
R& range,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
{
using It = std::ranges::iterator_t<R>;
using Se = std::ranges::sentinel_t<R>;
auto skipper_ctx = x3::make_context<skipper_tag>(as_parser(std::forward<Skipper>(s)));
std::optional<x3::expectation_failure<It>> expect_failure;
auto ctx = x3::make_context<expectation_failure_tag>(expect_failure, skipper_ctx);
using Context = decltype(ctx);
static_assert(Parsable<Parser, It, Se, Context, unused_type, Attribute>);
It first = std::ranges::begin(range);
Se last = std::ranges::end(range);
bool ok = as_parser(std::forward<Parser>(p)).parse(first, last, ctx, unused, attr);
if (post_skip == skip_flag::post_skip)
{
x3::skip_over(first, last, ctx);
if (expect_failure) ok = false;
}
return parse_result_for<R>{
.ok = ok,
.expect_failure = std::move(expect_failure),
.remainder = {std::move(first), std::move(last)}
};
}
template <
std::ranges::forward_range R,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Skipper,
typename Attribute
>
constexpr void
phrase_parse_main(
parse_result_for<R>& res,
R& range,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
{
using It = std::ranges::iterator_t<R>;
using Se = std::ranges::sentinel_t<R>;
auto skipper_ctx = x3::make_context<skipper_tag>(as_parser(std::forward<Skipper>(s)));
res.expect_failure.reset();
auto ctx = x3::make_context<expectation_failure_tag>(res.expect_failure, skipper_ctx);
using Context = decltype(ctx);
static_assert(Parsable<Parser, It, Se, Context, unused_type, Attribute>);
It first = std::ranges::begin(range);
Se last = std::ranges::end(range);
res.ok = as_parser(std::forward<Parser>(p)).parse(first, last, ctx, unused, attr);
if (post_skip == skip_flag::post_skip)
{
x3::skip_over(first, last, ctx);
if (res.expect_failure) res.ok = false;
}
res.remainder = {std::move(first), std::move(last)};
}
template <
std::ranges::forward_range R,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Skipper,
typename Attribute
>
[[nodiscard]] constexpr parse_result_for<R>
template <typename Iterator, typename Parser, typename Skipper, typename Attribute>
inline bool
phrase_parse(
R& range,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
Iterator& first
, Iterator last
, Parser const& p
, Skipper const& s
, Attribute& attr
, skip_flag post_skip = skip_flag::post_skip)
{
return x3::phrase_parse_main(range, std::forward<Parser>(p), std::forward<Skipper>(s), attr, post_skip);
return phrase_parse_main(first, last, p, s, attr, post_skip);
}
template <
std::ranges::forward_range R,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Skipper,
typename Attribute
>
[[nodiscard]] constexpr parse_result_for<R>
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser, typename Skipper, typename Attribute>
inline bool
phrase_parse(
R& range,
Parser&& p,
Skipper&& s,
skip_flag post_skip = skip_flag::post_skip
)
Iterator const& first_
, Iterator last
, Parser const& p
, Skipper const& s
, Attribute& attr
, skip_flag post_skip = skip_flag::post_skip)
{
return x3::phrase_parse_main(range, std::forward<Parser>(p), std::forward<Skipper>(s), unused, post_skip);
Iterator first = first_;
return phrase_parse_main(first, last, p, s, attr, post_skip);
}
template <
std::ranges::forward_range R,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Skipper,
typename Attribute
>
constexpr void
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser, typename Skipper>
inline bool
phrase_parse(
parse_result_for<R>& res,
R& range,
Parser&& p,
Skipper&& s,
Attribute& attr,
skip_flag post_skip = skip_flag::post_skip
)
Iterator& first
, Iterator last
, Parser const& p
, Skipper const& s
, skip_flag post_skip = skip_flag::post_skip)
{
return x3::phrase_parse_main(res, range, std::forward<Parser>(p), std::forward<Skipper>(s), attr, post_skip);
return phrase_parse_main(first, last, p, s, unused, post_skip);
}
template <
std::ranges::forward_range R,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Parser,
X3Parser<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>> Skipper,
typename Attribute
>
constexpr void
///////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Parser, typename Skipper>
inline bool
phrase_parse(
parse_result_for<R>& res,
R& range,
Parser&& p,
Skipper&& s,
skip_flag post_skip = skip_flag::post_skip
)
Iterator const& first_
, Iterator last
, Parser const& p
, Skipper const& s
, skip_flag post_skip = skip_flag::post_skip)
{
return x3::phrase_parse_main(res, range, std::forward<Parser>(p), std::forward<Skipper>(s), unused, post_skip);
Iterator first = first_;
return phrase_parse_main(first, last, p, s, unused, post_skip);
}
template <typename Skipper, typename ItOrRange, typename Se = void>
struct phrase_parse_context;
template <typename Skipper, typename ItOrRange, typename Se = void>
using phrase_parse_context_t = typename phrase_parse_context<Skipper, ItOrRange, Se>::type;
template <typename Skipper, std::forward_iterator It, class Se>
struct phrase_parse_context<Skipper, It, Se>
///////////////////////////////////////////////////////////////////////////
template <typename Skipper>
struct phrase_parse_context
{
using iterator_t = It;
using sentinel_t = std::conditional_t<std::is_void_v<Se>, It, Se>;
static_assert(X3Parser<Skipper, It, sentinel_t>);
using skipper_ctx_type = decltype(x3::make_context<skipper_tag>(as_parser(std::declval<Skipper const&>())));
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
using type = skipper_ctx_type;
#else
using type = decltype(x3::make_context<expectation_failure_tag>(
std::declval<std::optional<expectation_failure<It>>&>(),
std::declval<skipper_ctx_type const&>()
));
#endif
typedef decltype(
make_context<skipper_tag>(as_parser(std::declval<Skipper>())))
type;
};
template <typename Skipper, std::ranges::forward_range R>
struct phrase_parse_context<Skipper, R>
{
using iterator_t = std::ranges::iterator_t<R>;
using sentinel_t = std::ranges::sentinel_t<R>;
static_assert(X3Parser<Skipper, iterator_t, sentinel_t>);
using skipper_ctx_type = decltype(x3::make_context<skipper_tag>(as_parser(std::declval<Skipper const&>())));
using type = decltype(x3::make_context<expectation_failure_tag>(
std::declval<std::optional<expectation_failure<iterator_t>>&>(),
std::declval<skipper_ctx_type const&>()
));
};
template <typename ItOrRange, typename Se = void>
struct parse_context;
template <typename ItOrRange, typename Se = void>
using parse_context_t = typename parse_context<ItOrRange, Se>::type;
template <std::forward_iterator It>
struct parse_context<It>
{
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
using type = unused_type;
#else
using type = decltype(x3::make_context<expectation_failure_tag>(
std::declval<std::optional<expectation_failure<It>>&>()
));
#endif
};
template <std::ranges::forward_range R>
struct parse_context<R>
{
using type = decltype(x3::make_context<expectation_failure_tag>(
std::declval<std::optional<expectation_failure<std::ranges::iterator_t<R>>>&>()
));
};
} // boost::spirit::x3
}}}
#endif

View File

@@ -1,90 +0,0 @@
/*=============================================================================
Copyright (c) 2025 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)
=============================================================================*/
#ifndef BOOST_SPIRIT_X3_CORE_PARSE_RESULT_HPP
#define BOOST_SPIRIT_X3_CORE_PARSE_RESULT_HPP
#include <boost/spirit/home/x3/support/expectation.hpp>
#include <optional>
#include <iterator>
#include <ranges>
#include <string_view>
#include <type_traits>
namespace boost::spirit::x3
{
template <std::forward_iterator It, std::sentinel_for<It> Se = It>
struct parse_result
{
static_assert(
BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE == 0,
"Using this class requires `#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 0`"
);
using iterator_type = It;
using sentinel_type = Se;
// Strictly equivalent to the logical conjunction of the return
// values of the `.parse()` functions of all underlying parsers.
bool ok = false;
// Represents the failure of `x3::expect[p]` and `a > b`.
// Has value if and only if `ok` is `false` and any of
// the underlying parsers have encountered expectation
// failure.
std::optional<expectation_failure<It>> expect_failure;
// Represents the remaining subrange of the input, after
// applications of all (partially) successful attempts on
// underlying parsers.
std::ranges::subrange<It, Se> remainder;
// Convenient accessor, returns the string view of `remainder`.
[[nodiscard]] constexpr std::basic_string_view<std::iter_value_t<It>>
remainder_str() const
requires requires {
typename std::basic_string_view<std::iter_value_t<It>>;
requires std::is_constructible_v<std::basic_string_view<std::iter_value_t<It>>, std::ranges::subrange<It, Se> const&>;
}
{
return std::basic_string_view<std::iter_value_t<It>>{remainder};
}
[[nodiscard]] constexpr bool completed() const noexcept
{
// Cases:
// (1) The input was empty, and
// (1-a) The grammar accepts empty input and succeeded => remainder.empty() && ok
// (1-b) The grammar rejects empty input and failed => remainder.empty() && !ok
// (2) The input was NOT empty, and
// (2-a) The grammar comsumed all input and succeeded => remainder.empty() && ok
// (2-b) The grammar consumed some input and (partially) succeeded => !remainder.empty() && ok
// (2-c) The grammar consumed all input and failed (possibly via semantic action) => remainder.empty() && !ok
// (2-d) The grammar consumed some input and failed => !remainder.empty() && !ok
// `completed()` is `true` if and only if the parse result is
// either 1-a or 2-a; `false` otherwise. Note that 2-b is
// intentionally considered out of scope.
//
// This definition eliminates the need to double-check both
// the resulting `bool` and iterators on the user's part,
// which had been the long-standing burden of Spirit since
// the Qi era.
return ok && remainder.empty();
}
[[nodiscard]] constexpr explicit operator bool() const noexcept
{
return this->completed();
}
};
template <std::ranges::forward_range R>
using parse_result_for = parse_result<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>>;
} // boost::spirit::x3
#endif

View File

@@ -1,7 +1,6 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2013 Agustin Berge
Copyright (c) 2025 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)
@@ -9,22 +8,24 @@
#if !defined(BOOST_SPIRIT_X3_PARSER_OCTOBER_16_2008_0254PM)
#define BOOST_SPIRIT_X3_PARSER_OCTOBER_16_2008_0254PM
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/declval.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/traits/has_attribute.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/assert.hpp>
#include <iterator>
#include <string>
#include <type_traits>
#include <concepts>
#include <utility>
#if !defined(BOOST_SPIRIT_X3_NO_RTTI)
#include <typeinfo>
#endif
namespace boost::spirit::x3
namespace boost { namespace spirit { namespace x3
{
template <typename Subject, typename Action>
struct action;
@@ -32,74 +33,32 @@ namespace boost::spirit::x3
template <typename Subject, typename Handler>
struct guard;
namespace detail
{
struct parser_base {};
struct parser_id;
} // detail
struct parser_base {};
struct parser_id;
template <typename Derived>
struct parser : detail::parser_base
struct parser : parser_base
{
static_assert(!std::is_reference_v<Derived>);
using derived_type = Derived;
typedef Derived derived_type;
static bool const handles_container = false;
static bool const is_pass_through_unary = false;
static bool const has_action = false;
static constexpr bool handles_container = false;
static constexpr bool is_pass_through_unary = false;
static constexpr bool has_action = false;
[[nodiscard]] constexpr Derived& derived() & noexcept
constexpr Derived const& derived() const
{
return static_cast<Derived&>(*this);
return *static_cast<Derived const*>(this);
}
[[nodiscard]] constexpr Derived const& derived() const& noexcept
template <typename Action>
constexpr action<Derived, Action> operator[](Action f) const
{
return static_cast<Derived const&>(*this);
return { this->derived(), f };
}
[[nodiscard]] constexpr Derived&& derived() && noexcept
template <typename Handler>
constexpr guard<Derived, Handler> on_error(Handler f) const
{
return static_cast<Derived&&>(*this);
}
[[nodiscard]] constexpr Derived const&& derived() const&& noexcept
{
return static_cast<Derived const&&>(*this);
}
template <typename Self, typename Action>
requires std::is_constructible_v<
action<Derived, std::remove_cvref_t<Action>>,
decltype(std::declval<Self>().derived()),
Action
>
[[nodiscard]] constexpr action<Derived, std::remove_cvref_t<Action>>
operator[](this Self&& self, Action&& f)
noexcept(std::is_nothrow_constructible_v<
action<Derived, std::remove_cvref_t<Action>>,
decltype(std::forward<Self>(self).derived()),
Action
>)
{
return { std::forward<Self>(self).derived(), std::forward<Action>(f) };
}
template <typename Self, typename Handler>
requires std::is_constructible_v<
guard<Derived, std::remove_cvref_t<Handler>>,
decltype(std::declval<Self>().derived()),
Handler
>
[[nodiscard]] constexpr guard<Derived, std::remove_cvref_t<Handler>>
on_error(this Self&& self, Handler&& f)
noexcept(std::is_nothrow_constructible_v<
guard<Derived, std::remove_cvref_t<Handler>>,
decltype(std::forward<Self>(self).derived()),
Handler
>)
{
return { std::forward<Self>(self).derived(), std::forward<Handler>(f) };
return { this->derived(), f };
}
};
@@ -109,20 +68,14 @@ namespace boost::spirit::x3
template <typename Subject, typename Derived>
struct unary_parser : parser<Derived>
{
using category = unary_category;
using subject_type = Subject;
static constexpr bool has_action = Subject::has_action;
typedef unary_category category;
typedef Subject subject_type;
static bool const has_action = Subject::has_action;
template <typename SubjectT>
requires
(!std::is_same_v<std::remove_cvref_t<SubjectT>, unary_parser>) &&
std::is_constructible_v<Subject, SubjectT>
constexpr unary_parser(SubjectT&& subject)
noexcept(std::is_nothrow_constructible_v<Subject, SubjectT>)
: subject(std::forward<SubjectT>(subject))
{}
constexpr unary_parser(Subject const& subject)
: subject(subject) {}
[[nodiscard]] constexpr unary_parser const& get_unary() const noexcept { return *this; }
unary_parser const& get_unary() const { return *this; }
Subject subject;
};
@@ -130,365 +83,139 @@ namespace boost::spirit::x3
template <typename Left, typename Right, typename Derived>
struct binary_parser : parser<Derived>
{
using category = binary_category;
using left_type = Left;
using right_type = Right;
static constexpr bool has_action = left_type::has_action || right_type::has_action;
typedef binary_category category;
typedef Left left_type;
typedef Right right_type;
static bool const has_action =
left_type::has_action || right_type::has_action;
template <typename LeftT, typename RightT>
requires std::is_constructible_v<Left, LeftT> && std::is_constructible_v<Right, RightT>
constexpr binary_parser(LeftT&& left, RightT&& right)
noexcept(std::is_nothrow_constructible_v<Left, LeftT> && std::is_nothrow_constructible_v<Right, RightT>)
: left(std::forward<LeftT>(left))
, right(std::forward<RightT>(right))
{}
constexpr binary_parser(Left const& left, Right const& right)
: left(left), right(right) {}
[[nodiscard]] constexpr binary_parser const& get_binary() const noexcept { return *this; }
binary_parser const& get_binary() const { return *this; }
Left left;
Right right;
};
///////////////////////////////////////////////////////////////////////////
// as_parser: convert a type, T, into a parser.
///////////////////////////////////////////////////////////////////////////
namespace extension
{
// In short: if you want to customize the `as_parser(p)` behavior, just
// specialize `x3::extension::as_parser` for your class.
//
// Older versions of X3 specified the signature `as_spirit_parser(p)` to be
// used for the ADL adaptation of user-defined types. However, many parts in
// X3 were instead using ADL of the form `as_parser(p)` to dispatch any types
// in the first place. This makes it impossible for the control to reach the
// `as_spirit_parser(p)` call if any user-defined `as_parser(p)` is *more
// constrained* than `as_spirit_parser(p)`.
//
// In other words, the old signature `as_spirit_parser(p)` has never been
// implemented correctly since its very first introduction to X3.
//
// Additionally, GitHub fulltext search shows that there exists zero usage of
// `as_spirit_parser` except for the unmaintained blog post written by the
// original inventor. Therefore, we simply removed the feature.
//
// Furthermore, the current maintainer of X3 believes there are no practical
// use cases of ADL adaptation on user-defined types. As such, we make the
// `x3::as_parser` (not to be confused with `x3::extension::as_parser`) to
// model a C++20-ish CPO to inhibit undesired ADL in the first place.
namespace detail
{
namespace as_parser_guard
{
void as_spirit_parser(...);
template <typename T>
struct as_parser; // not defined
template<typename T, typename R =
decltype(as_spirit_parser(boost::declval<T const&>()))>
struct deduce_as_parser
{
typedef R type;
typedef typename
boost::remove_cv<
typename boost::remove_reference<R>::type
>::type
value_type;
static type call(T const& v)
{
return as_spirit_parser(v);
}
};
template<typename T>
struct deduce_as_parser<T, void>
{};
}
using as_parser_guard::deduce_as_parser;
}
template <typename T, typename Enable = void>
struct as_parser : detail::deduce_as_parser<T> {};
template <>
struct as_parser<unused_type>
{
using value_type [[deprecated("Use x3::as_parser_plain_t")]] = unused_type;
template <typename T>
[[nodiscard]] static constexpr auto&& call(T&& unused_) noexcept
typedef unused_type type;
typedef unused_type value_type;
static constexpr type call(unused_type)
{
return std::forward<T>(unused_);
return unused;
}
};
template <typename Derived>
requires std::is_base_of_v<detail::parser_base, std::remove_cvref_t<Derived>>
struct as_parser<Derived>
struct as_parser<Derived
, typename enable_if<is_base_of<parser_base, Derived>>::type>
{
using value_type [[deprecated("Use x3::as_parser_plain_t")]] = std::remove_cvref_t<Derived>;
template <typename T>
[[nodiscard]] static constexpr auto&& call(T&& p) noexcept
typedef Derived const& type;
typedef Derived value_type;
static constexpr type call(Derived const& p)
{
return std::forward<T>(p);
return p;
}
};
template <typename Derived>
struct as_parser<parser<Derived>>
{
using value_type [[deprecated("Use x3::as_parser_plain_t")]] = Derived;
template <typename T>
[[nodiscard]] static constexpr auto&& call(T&& p) noexcept
typedef Derived const& type;
typedef Derived value_type;
static constexpr type call(parser<Derived> const& p)
{
return std::forward<T>(p).derived();
return p.derived();
}
};
}
namespace detail
template <typename T>
constexpr typename extension::as_parser<T>::type
as_parser(T const& x)
{
struct as_parser_fn
{
template <typename T>
static void operator()(T&&) = delete; // If you reach here, your specialization of `x3::extension::as_parser` has a wrong signature, or the type is simply incompatible.
return extension::as_parser<T>::call(x);
}
// catch-all default fallback
template <typename T>
requires std::is_base_of_v<
parser_base,
std::remove_cvref_t<decltype(extension::as_parser<std::remove_cvref_t<T>>::call(std::declval<T>()))>
>
[[nodiscard]] static constexpr decltype(auto)
operator()(T&& x) noexcept(noexcept(extension::as_parser<std::remove_cvref_t<T>>::call(std::forward<T>(x))))
{
return extension::as_parser<std::remove_cvref_t<T>>::call(std::forward<T>(x));
}
template <typename Derived>
[[nodiscard]] static constexpr auto&&
operator()(parser<Derived>& p) noexcept
{
return p.derived();
}
template <typename Derived>
[[nodiscard]] static constexpr auto&&
operator()(parser<Derived> const& p) noexcept
{
return p.derived();
}
template <typename Derived>
[[nodiscard]] static constexpr auto&&
operator()(parser<Derived>&& p) noexcept
{
return std::move(p).derived();
}
template <typename Derived>
[[nodiscard]] static constexpr auto&&
operator()(parser<Derived> const&& p) noexcept
{
return std::move(p).derived();
}
}; // as_parser_fn
} // detail
inline namespace cpos
template <typename Derived>
constexpr Derived const&
as_parser(parser<Derived> const& p)
{
inline constexpr detail::as_parser_fn as_parser{};
} // cpos
return p.derived();
}
template <typename T>
using as_parser_t = decltype(as_parser(std::declval<T>())); // no ADL
template <typename T>
using as_parser_plain_t = std::remove_cvref_t<as_parser_t<T>>;
// This is a very low level API provided for consistency with
// `is_parser_nothrow_castable`. Most users should use `X3Subject`
// instead.
template <typename T>
struct is_parser_castable : std::bool_constant<
requires {
{ x3::as_parser(std::declval<T>()) };
}
> {};
template <typename T>
constexpr bool is_parser_castable_v = is_parser_castable<T>::value;
// This trait can be used primarily for multi-parameter constructors.
// For example, `a op b` normally requires the below condition to
// become fully noexcept:
// is_parser_nothrow_castable_v<A> &&
// is_parser_nothrow_castable_v<B> &&
// std::is_nothrow_constructible_v<op_parser, as_parser_t<A>, as_parser_t<B>>
template <typename T>
struct is_parser_nothrow_castable : std::bool_constant<
requires {
{ x3::as_parser(std::declval<T>()) } noexcept;
}
> {};
template <typename T>
constexpr bool is_parser_nothrow_castable_v = is_parser_nothrow_castable<T>::value;
// A type that models `X3Subject` can be used in generic directives
// and operators. Note that this concept is iterator-agnostic.
///////////////////////////////////////////////////////////////////////////
// The main what function
//
// For example, let `p` denote an object of `T`. Then, `!p` is a
// well-formed NOT predicate in X3's domain (with "NOT predicate"
// referring to that of the PEG semantics) if and only if `T`
// models `X3Subject`.
template <typename T>
concept X3Subject =
is_parser_castable_v<T> && // `as_parser(t)` is valid?
std::is_base_of_v<detail::parser_base, as_parser_plain_t<T>>;
// Checks whether `Parser(as_parser(t))` is valid.
//
// This trait can be used for checking whether a "Parser" is constructible
// with some arbitrary argument `T`. In our (X3 core) use cases, the `Parser`
// is usually a concrete type (e.g. `some_parser<as_parser_plain_t<Subject>>`)
// whereas the `T` is some user-provided arbitrary type (e.g. `X3Subject Subject`).
//
// This interface can only be used to check whether `Parser`'s single-parameter
// constructor is available. For multi-parameter construction, manually combine
// `is_parser_castable` with `std::is_constructible`.
template <X3Subject Parser, X3Subject T>
struct is_parser_constructible : std::false_type {};
template <X3Subject Parser, X3Subject T>
requires std::is_constructible_v<Parser, as_parser_t<T>>
struct is_parser_constructible<Parser, T> : std::true_type {};
template <X3Subject Parser, X3Subject T>
constexpr bool is_parser_constructible_v = is_parser_constructible<Parser, T>::value;
// Checks whether `Parser(as_parser(t))` is noexcept.
//
// This interface can only be used to check whether `Parser`'s single-parameter
// constructor is available. For multi-parameter construction, manually combine
// `is_parser_nothrow_castable` with `std::is_nothrow_constructible`.
template <X3Subject Parser, X3Subject T>
struct is_parser_nothrow_constructible : std::false_type {};
template <X3Subject Parser, X3Subject T>
requires
is_parser_nothrow_castable_v<T> &&
std::is_nothrow_constructible_v<Parser, as_parser_t<T>>
struct is_parser_nothrow_constructible<Parser, T> : std::true_type {};
template <X3Subject Parser, X3Subject T>
constexpr bool is_parser_nothrow_constructible_v = is_parser_nothrow_constructible<Parser, T>::value;
template <
typename Parser,
typename It, // Don't constrain these; just let `static_assert` be engaged for friendly errors
typename Se,
typename Context,
typename RContext,
typename Attribute
>
struct is_parsable : std::bool_constant<
requires(std::remove_cvref_t<as_parser_t<Parser>> const& p) // mutable parser use case is currently unknown
{
{
p.parse(
std::declval<It&>(), // first
std::declval<Se>(), // last
std::declval<Context const&>(), // context
std::declval<RContext&>(), // rcontext
std::declval<Attribute&>() // attr
)
} -> std::convertible_to<bool>;
}
> {
static_assert(X3Subject<Parser>);
static_assert(!std::is_reference_v<It>);
static_assert(std::forward_iterator<It>);
static_assert(std::sentinel_for<Se, It>);
static_assert(!std::is_reference_v<Context>);
static_assert(!std::is_reference_v<RContext>);
static_assert(!std::is_reference_v<Attribute>);
};
template <typename Parser, typename It, typename Se, typename Context, typename RContext, typename Attribute>
constexpr bool is_parsable_v = is_parsable<Parser, It, Se, Context, RContext, Attribute>::value;
// TODO: handle `parse_into_container` case
template <typename Parser, typename It, typename Se, typename Context, typename RContext, typename Attribute>
concept Parsable = is_parsable<Parser, It, Se, Context, RContext, Attribute>::value;
// ^^^ this must be concept in order to provide better diagnostics (e.g. on MSVC)
template <
typename Parser,
typename It, // Don't constrain these; just let `static_assert` be engaged for friendly errors
typename Se,
typename Context,
typename RContext,
typename Attribute
>
struct is_nothrow_parsable : std::bool_constant<
requires(std::remove_cvref_t<as_parser_t<Parser>> const& p) // mutable parser use case is currently unknown
{
{
p.parse(
std::declval<It&>(), // first
std::declval<Se>(), // last
std::declval<Context const&>(), // context
std::declval<RContext&>(), // rcontext
std::declval<Attribute&>() // attr
)
} noexcept -> std::convertible_to<bool>;
}
> {};
template <typename Parser, typename It, typename Se, typename Context, typename RContext, typename Attribute>
constexpr bool is_nothrow_parsable_v = is_nothrow_parsable<Parser, It, Se, Context, RContext, Attribute>::value;
// The primary "parser" concept of X3, applicable in iterator-aware contexts.
//
// Let `it` denote an lvalue reference of `It`, and let `se` denote a prvalue of `Se`.
// Let `p` denote an lvalue reference of `Parser`.
//
// For `Parser` to model `X3Parser`, the following conditions must be satisfied:
// -- the expression `x3::as_parser(p)` is well-formed in unevaluated context, and
// -- the expression `cp.parse(it, se, x3::unused, x3::unused, x3::unused)`
// is well-formed and the return type is convertible to `bool`, where `cp` denotes a
// const lvalue reference to the result of the expression `x3::as_parser(p)`.
//
// Although some exotic user-defined parser could be designed to operate on the very
// specific context type and/or attribute type, we require the parser to at least
// accept `unused_type` for `RContext`, `Context` and `Attribute`. This is because
// core parsers of Spirit have historically been assuming natural use of `unused_type`
// in many locations.
template <typename Parser, class It, class Se>
concept X3Parser =
X3Subject<Parser> &&
is_parsable_v<Parser, It, Se, unused_type, unused_type, unused_type>;
// The runtime type info that can be obtained via `x3::what(p)`.
template <X3Subject Subject, typename Enabled = void>
// Note: unlike Spirit2, spirit parsers are no longer required to have a
// "what" member function. In X3, we specialize the get_info struct
// below where needed. If a specialization is not provided, the default
// below will be used. The default "what" result will be the typeid
// name of the parser if BOOST_SPIRIT_X3_NO_RTTI is not defined, otherwise
// "undefined"
///////////////////////////////////////////////////////////////////////////
template <typename Parser, typename Enable = void>
struct get_info
{
[[nodiscard]] static constexpr std::string operator()(Subject const& subject)
typedef std::string result_type;
std::string operator()(Parser const&) const
{
if constexpr (requires {
{ subject.get_x3_info() } -> std::convertible_to<std::string>;
})
{
return subject.get_x3_info();
}
else
{
(void)subject;
#ifndef BOOST_SPIRIT_X3_NO_RTTI
return typeid(Subject).name();
#else
return "(get_info undefined)";
#endif
}
#if !defined(BOOST_SPIRIT_X3_NO_RTTI)
return typeid(Parser).name();
#else
return "undefined";
#endif
}
};
namespace detail
template <typename Parser>
std::string what(Parser const& p)
{
// "what" is an extremely common identifier that can be defined in many user-specific
// namespaces. We should avoid ADL usage for such generic names in the first place.
// (Note: CPO inhibits ADL in general.)
struct what_fn
{
template <X3Subject Subject>
[[nodiscard]] static constexpr std::string operator()(Subject const& p)
{
return get_info<Subject>{}(p);
}
};
} // detail
return get_info<Parser>()(p);
}
}}}
inline namespace cpos
{
inline constexpr detail::what_fn what{}; // no ADL
} // cpos
} // boost::spirit::x3
namespace boost::spirit::x3::traits
namespace boost { namespace spirit { namespace x3 { namespace traits
{
template <typename Subject, typename Derived, typename Context>
struct has_attribute<x3::unary_parser<Subject, Derived>, Context>
@@ -496,7 +223,8 @@ namespace boost::spirit::x3::traits
template <typename Left, typename Right, typename Derived, typename Context>
struct has_attribute<x3::binary_parser<Left, Right, Derived>, Context>
: std::disjunction<has_attribute<Left, Context>, has_attribute<Right, Context>> {};
} // boost::spirit::x3::traits
: mpl::bool_<has_attribute<Left, Context>::value ||
has_attribute<Right, Context>::value> {};
}}}}
#endif

View File

@@ -1,7 +1,6 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2001-2011 Hartmut Kaiser
Copyright (c) 2025 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)
@@ -14,12 +13,10 @@
#include <type_traits>
#include <utility>
namespace boost::spirit::x3
///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace x3
{
namespace detail
{
struct parser_id;
}
struct parser_id;
template <typename Exposed, typename Transformed>
struct default_transform_attribute
@@ -76,12 +73,13 @@ namespace boost::spirit::x3
template <typename Attribute>
struct transform_attribute<Attribute const, unused_type>
: transform_attribute<unused_type, unused_type> {};
} // boost::spirit::x3
}}}
namespace boost::spirit::x3::traits
///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace x3 { namespace traits
{
template <typename Exposed, typename Transformed>
struct transform_attribute<Exposed, Transformed, x3::detail::parser_id>
struct transform_attribute<Exposed, Transformed, x3::parser_id>
: x3::transform_attribute<Exposed, Transformed>
{
static_assert(!std::is_reference<Exposed>::value,
@@ -89,6 +87,6 @@ namespace boost::spirit::x3::traits
static_assert(!std::is_reference<Transformed>::value,
"Transformed cannot be a reference type");
};
} // boost::spirit::x3::traits
}}}}
#endif

View File

@@ -1,6 +1,5 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2025 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)
@@ -20,7 +19,7 @@
#include <typeinfo>
#endif
namespace boost::spirit::x3
namespace boost { namespace spirit { namespace x3
{
// default parse_rule implementation
template <typename ID, typename Iterator
@@ -148,7 +147,7 @@ namespace boost::spirit::x3
"The rule does not have an attribute. Check your parser.");
using transform = traits::transform_attribute<
Attribute_, attribute_type, detail::parser_id>;
Attribute_, attribute_type, parser_id>;
using transform_attr = typename transform::type;
transform_attr attr_ = transform::pre(attr);
@@ -256,6 +255,6 @@ namespace boost::spirit::x3
/***/
} // boost::spirit::x3
}}}
#endif

View File

@@ -0,0 +1,78 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2013 Agustin Berge
http://spirit.sourceforge.net/
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_X3_SUBCONTEXT_APR_15_2013_0840AM)
#define BOOST_SPIRIT_X3_SUBCONTEXT_APR_15_2013_0840AM
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
namespace boost { namespace spirit { namespace x3
{
template <typename... T>
struct subcontext;
template <>
struct subcontext<>
{
template <typename Context>
subcontext(Context const& /*context*/)
{}
template <typename ID_>
unused_type
get(ID_) const
{
return unused;
}
};
template <typename T>
struct subcontext<T>
: context<typename T::first_type, typename T::second_type>
{
typedef context<
typename T::first_type, typename T::second_type
> context_type;
template <typename Context>
subcontext(Context const& context)
: context_type(x3::get<typename T::first_type>(context))
{}
using context_type::get;
};
template <typename T, typename... Tail>
struct subcontext<T, Tail...>
: subcontext<Tail...>
, context<
typename T::first_type, typename T::second_type
, subcontext<Tail...>
>
{
typedef subcontext<Tail...> base_type;
typedef context<
typename T::first_type, typename T::second_type
, base_type
> context_type;
template <typename Context>
subcontext(Context const& context)
: base_type(context)
, context_type(
x3::get<typename T::first_type>(context)
, *static_cast<base_type*>(this))
{}
using context_type::get;
};
}}}
#endif

View File

@@ -1,6 +1,6 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2025 Nana Sakisaka
http://spirit.sourceforge.net/
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)
@@ -8,27 +8,20 @@
#if !defined(BOOST_SPIRIT_X3_IS_RANGE_DEC_06_2017_1900PM)
#define BOOST_SPIRIT_X3_IS_RANGE_DEC_06_2017_1900PM
#include <boost/range/range_fwd.hpp> // TODO: remove this
#include <boost/range/range_fwd.hpp>
#include <boost/mpl/bool.hpp>
#include <iterator>
#include <ranges>
#include <type_traits>
namespace boost::spirit::x3::traits
namespace boost { namespace spirit { namespace x3 { namespace traits
{
template <typename T, typename Enable = void>
struct is_range : std::false_type {};
struct is_range
: mpl::false_
{};
template <typename T>
constexpr bool is_range_v = is_range<T>::value;
template <typename T>
struct [[deprecated("Use std::ranges::subrange")]]
is_range<boost::iterator_range<T>> : std::true_type {};
template <std::input_or_output_iterator It, std::sentinel_for<It> Se, std::ranges::subrange_kind Kind>
struct is_range<std::ranges::subrange<It, Se, Kind>> : std::true_type {};
} // boost::spirit::x3::traits
struct is_range<boost::iterator_range<T>>
: mpl::true_
{};
}}}}
#endif

View File

@@ -1,21 +1,19 @@
/*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman
Copyright (c) 2025 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 "test.hpp"
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include "test.hpp"
int main()
int
main()
{
using spirit_test::test;
namespace x3 = boost::spirit::x3;
using x3::eoi;
using boost::spirit::x3::eoi;
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(eoi);
@@ -25,7 +23,7 @@ int main()
}
{
BOOST_TEST(x3::what(eoi) == "eoi");
BOOST_TEST(what(eoi) == "eoi");
}
return boost::report_errors();

View File

@@ -1,21 +1,19 @@
/*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman
Copyright (c) 2025 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 "test.hpp"
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include "test.hpp"
int main()
int
main()
{
using spirit_test::test;
namespace x3 = boost::spirit::x3;
using x3::eol;
using boost::spirit::x3::eol;
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(eol);
@@ -28,7 +26,7 @@ int main()
}
{
BOOST_TEST(x3::what(eol) == "eol");
BOOST_TEST(what(eol) == "eol");
}
return boost::report_errors();

View File

@@ -1,59 +1,49 @@
/*=============================================================================
Copyright (c) 2001-2017 Joel de Guzman
Copyright (c) 2017 think-cell GmbH
Copyright (c) 2025 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)
=============================================================================*/
// TODO: full support for `std::ranges::subrange` requires follow-up PR
//#ifndef BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE
//# define BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE
//#endif
#include <boost/spirit/home/x3.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <ranges>
#include <algorithm>
#include <iostream>
#include <string>
#include <functional>
int main()
{
using boost::adaptors::transform;
using boost::spirit::x3::raw;
using boost::spirit::x3::eps;
using boost::spirit::x3::eoi;
using boost::spirit::x3::standard::upper;
using boost::spirit::x3::upper;
using boost::spirit::x3::repeat;
using boost::spirit::x3::parse;
std::string input = "abcde";
auto const rng = input | std::views::transform([](char c) {
std::function<char(char)> func = [](char c) {
return c < 'a' || 'z' < c ? c : static_cast<char>(c - 'a' + 'A');
});
using range_type = decltype(rng);
static_assert(std::ranges::forward_range<range_type>);
};
auto const rng = transform(input, func);
{
std::string str;
BOOST_TEST((parse(std::ranges::begin(rng), std::ranges::end(rng), +upper >> eoi, str)));
BOOST_TEST((parse(boost::begin(rng), boost::end(rng), +upper >> eoi, str)));
BOOST_TEST(("ABCDE"==str));
}
{
// TODO: full support for `std::ranges::subrange` requires follow-up PR
//std::ranges::subrange<std::ranges::iterator_t<range_type>> str;
boost::iterator_range<std::ranges::iterator_t<range_type>> str;
BOOST_TEST((parse(std::ranges::begin(rng), std::ranges::end(rng), raw[+upper >> eoi], str)));
BOOST_TEST((std::ranges::equal(std::string("ABCDE"), str)));
boost::iterator_range<decltype(boost::begin(rng))> str;
BOOST_TEST((parse(boost::begin(rng), boost::end(rng), raw[+upper >> eoi], str)));
BOOST_TEST((boost::equal(std::string("ABCDE"), str)));
}
{
BOOST_TEST((parse(std::ranges::begin(rng), std::ranges::end(rng), (repeat(6)[upper] | repeat(5)[upper]) >> eoi)));
BOOST_TEST((parse(boost::begin(rng), boost::end(rng), (repeat(6)[upper] | repeat(5)[upper]) >> eoi)));
}
return boost::report_errors();

View File

@@ -1,6 +1,5 @@
/*=============================================================================
Copyright (c) 2019 Nikita Kniazev
Copyright (c) 2025 Nana Sakisaka
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
@@ -13,37 +12,31 @@
namespace unused_attr {
auto const skipper_def = x3::standard::lit('*');
const auto skipper_def = x3::lit('*');
BOOST_SPIRIT_DEFINE(skipper)
BOOST_SPIRIT_INSTANTIATE(skipper_type, char const*, x3::unused_type)
auto const skipper2_def = x3::standard::lit('#');
const auto skipper2_def = x3::lit('#');
BOOST_SPIRIT_DEFINE(skipper2)
BOOST_SPIRIT_INSTANTIATE(skipper2_type, char const*, x3::unused_type)
auto const grammar_def = *x3::standard::lit('=');
const auto grammar_def = *x3::lit('=');
BOOST_SPIRIT_DEFINE(grammar)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::unused_type)
using skipper_context_type = typename x3::phrase_parse_context<skipper_type, char const*>::type;
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, skipper_context_type)
using skipper2_context_type = typename x3::phrase_parse_context<skipper2_type, char const*>::type;
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, skipper2_context_type)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::phrase_parse_context<skipper_type>::type)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::phrase_parse_context<skipper2_type>::type)
}
namespace used_attr {
auto const skipper_def = x3::standard::space;
const auto skipper_def = x3::space;
BOOST_SPIRIT_DEFINE(skipper)
BOOST_SPIRIT_INSTANTIATE(skipper_type, char const*, x3::unused_type)
auto const grammar_def = x3::int_;
const auto grammar_def = x3::int_;
BOOST_SPIRIT_DEFINE(grammar)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::unused_type)
using skipper_context_type = typename x3::phrase_parse_context<skipper_type, char const*>::type;
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, skipper_context_type)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::phrase_parse_context<skipper_type>::type)
}