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:
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
78
include/boost/spirit/home/x3/support/subcontext.hpp
Normal file
78
include/boost/spirit/home/x3/support/subcontext.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user