mirror of
https://github.com/boostorg/parser.git
synced 2026-01-19 04:22:13 +00:00
Add a type trait, "attribute", and associated alias attribute_t, that provide
the attribute type for a parser Parser used to parse range R. Fixes #111.
This commit is contained in:
@@ -105,6 +105,9 @@
|
||||
[def _cbp_ [funcref boost::parser::callback_parse `callback_parse()`]]
|
||||
[def _cbpp_ [funcref boost::parser::callback_prefix_parse `callback_prefix_parse()`]]
|
||||
|
||||
[def _attr_ [classref boost::parser::attribute `attribute`]]
|
||||
[def _attr_t_ [classref boost::parser::attribute_t `attribute_t`]]
|
||||
|
||||
[def _w_glb_ [funcref boost::parser::with_globals `with_globals()`]]
|
||||
[def _w_eh_ [funcref boost::parser::with_error_handler `with_error_handler()`]]
|
||||
|
||||
|
||||
@@ -66,10 +66,9 @@ subparsers does.
|
||||
_Parser_ parsers each have an /attribute/ associated with them, or explicitly
|
||||
have no attribute. An attribute is a value that the parser generates when it
|
||||
matches the input. For instance, the parser _d_ generates a `double` when it
|
||||
matches the input. Since it is not possible to write a type trait that
|
||||
returns the attribute type of a parser, we need notation for concisely
|
||||
communicating that relationship. _ATTR_ is a notional macro that expands to
|
||||
the attribute type of the parser passed to it; `_ATTR_np_(_d_)` is `double`.
|
||||
matches the input. _ATTR_ is a notional macro that expands to the attribute
|
||||
type of the parser passed to it; `_ATTR_np_(_d_)` is `double`. This is
|
||||
similar to the _attr_ type trait.
|
||||
|
||||
Next, we'll look at some simple programs that parse using _Parser_. We'll
|
||||
start small and build up from there.
|
||||
@@ -151,6 +150,10 @@ In fact, the return type of this call to _p_ is
|
||||
`std::nullopt` is returned. We'll look at how _Parser_ maps the type of the
|
||||
parser to the return type, or the filled in out-parameter's type, a bit later.
|
||||
|
||||
[note There's a type trait that can tell you the attribute type for a parser,
|
||||
_attr_ (and an associated alias _attr_t_). We'll discuss it more in the
|
||||
_attr_gen_ section.]
|
||||
|
||||
If I run it in a shell, this is the result:
|
||||
|
||||
[pre
|
||||
@@ -1326,6 +1329,30 @@ parse.]
|
||||
moved, copy, and default constructed. There is no support for move-only or
|
||||
non-default-constructible types.]
|
||||
|
||||
[heading The attribute type trait, _attr_]
|
||||
|
||||
You can use _attr_ (and the associated alias, _attr_t_) to determine the
|
||||
attribute a parser would have if it were passed to _p_. Since at least one
|
||||
parser (_ch_) has a polymorphic attribute type, _attr_ also takes the type of
|
||||
the range being parsed. If a parser produces no attribute, _attr_ will
|
||||
produce _n_, not `void`.
|
||||
|
||||
If you want to feed an iterator/sentinel pair to _attr_, create a range from
|
||||
it like so:
|
||||
|
||||
constexpr auto parser = /* ... */;
|
||||
auto first = /* ... */;
|
||||
auto const last = /* ... */;
|
||||
|
||||
namespace bp = boost::parser;
|
||||
// You can of course use std::ranges::subrange directly in C++20 and later.
|
||||
using attr_type = bp::attribute_t<decltype(BOOST_PARSER_SUBRANGE(first, last)), decltype(parser)>;
|
||||
|
||||
There is no single attribute type for any parser, since a parser can be placed
|
||||
within _omit_, which makes its attribute type _n_. Therefore, _attr_ cannot
|
||||
tell you what attribute your parser will produce under all circumstances; it
|
||||
only tells you what it would produce if it were passed to _p_.
|
||||
|
||||
[heading Parser attributes]
|
||||
|
||||
[table_attribute_generation]
|
||||
|
||||
@@ -373,6 +373,8 @@ namespace boost { namespace parser {
|
||||
template<typename T>
|
||||
using print_type = typename print_t<T>::type;
|
||||
|
||||
template<typename R, typename Parser>
|
||||
struct attribute_impl;
|
||||
|
||||
// Utility types.
|
||||
|
||||
@@ -1459,6 +1461,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
inline bool make_parse_result(nope &, bool success) { return success; }
|
||||
inline bool make_parse_result(none &, bool success) { return success; }
|
||||
|
||||
template<typename LoType, typename HiType>
|
||||
struct char_pair
|
||||
@@ -2039,27 +2042,7 @@ namespace boost { namespace parser {
|
||||
// API implementations
|
||||
|
||||
template<typename Iter, typename Sentinel, typename Parser>
|
||||
auto has_attribute(Iter first, Sentinel last, Parser parser)
|
||||
{
|
||||
constexpr auto flags = detail::flags::gen_attrs;
|
||||
using context = decltype(detail::make_context(
|
||||
first,
|
||||
last,
|
||||
std::declval<bool &>(),
|
||||
std::declval<int &>(),
|
||||
parser.error_handler_,
|
||||
parser.globals_,
|
||||
std::declval<detail::symbol_table_tries_t &>()));
|
||||
using attr_t = decltype(parser(
|
||||
std::false_type{},
|
||||
first,
|
||||
last,
|
||||
std::declval<context>(),
|
||||
detail::null_parser{},
|
||||
flags,
|
||||
std::declval<bool &>()));
|
||||
return std::integral_constant<bool, !is_nope_v<attr_t>>{};
|
||||
}
|
||||
auto has_attribute(Iter first, Sentinel last, Parser parser);
|
||||
|
||||
template<typename BaseIter, typename Iter>
|
||||
struct scoped_base_assign
|
||||
@@ -2151,14 +2134,9 @@ namespace boost { namespace parser {
|
||||
auto const flags =
|
||||
Debug ? detail::enable_trace(detail::flags::gen_attrs)
|
||||
: detail::flags::gen_attrs;
|
||||
using attr_t = decltype(parser(
|
||||
std::false_type{},
|
||||
first,
|
||||
last,
|
||||
context,
|
||||
detail::null_parser{},
|
||||
flags,
|
||||
success));
|
||||
using attr_t = typename detail::attribute_impl<
|
||||
BOOST_PARSER_SUBRANGE<std::remove_const_t<Iter>, Sentinel>,
|
||||
Parser>::type;
|
||||
try {
|
||||
attr_t attr_ = parser(
|
||||
std::false_type{},
|
||||
@@ -2317,8 +2295,9 @@ namespace boost { namespace parser {
|
||||
Debug ? detail::enable_trace(detail::default_flags())
|
||||
: detail::default_flags();
|
||||
detail::skip(first, last, skip, flags);
|
||||
using attr_t = decltype(parser(
|
||||
std::false_type{}, first, last, context, skip, flags, success));
|
||||
using attr_t = typename detail::attribute_impl<
|
||||
BOOST_PARSER_SUBRANGE<std::remove_const_t<Iter>, Sentinel>,
|
||||
Parser>::type;
|
||||
try {
|
||||
attr_t attr_ = parser(
|
||||
std::false_type{},
|
||||
@@ -5969,7 +5948,7 @@ namespace boost { namespace parser {
|
||||
constexpr char_parser(Expected expected) : expected_(expected) {}
|
||||
|
||||
template<typename T>
|
||||
using attribute_t = std::conditional_t<
|
||||
using attribute_type = std::conditional_t<
|
||||
std::is_same_v<AttributeType, void>,
|
||||
std::decay_t<T>,
|
||||
AttributeType>;
|
||||
@@ -5987,9 +5966,9 @@ namespace boost { namespace parser {
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success) const -> attribute_t<decltype(*first)>
|
||||
bool & success) const -> attribute_type<decltype(*first)>
|
||||
{
|
||||
attribute_t<decltype(*first)> retval;
|
||||
attribute_type<decltype(*first)> retval;
|
||||
call(use_cbs, first, last, context, skip, flags, success, retval);
|
||||
return retval;
|
||||
}
|
||||
@@ -6018,7 +5997,7 @@ namespace boost { namespace parser {
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
attribute_t<decltype(*first)> const x = *first;
|
||||
attribute_type<decltype(*first)> const x = *first;
|
||||
if (detail::unequal(context, x, expected_)) {
|
||||
success = false;
|
||||
return;
|
||||
@@ -6140,7 +6119,7 @@ namespace boost { namespace parser {
|
||||
constexpr digit_parser() {}
|
||||
|
||||
template<typename T>
|
||||
using attribute_t = std::decay_t<T>;
|
||||
using attribute_type = std::decay_t<T>;
|
||||
|
||||
template<
|
||||
bool UseCallbacks,
|
||||
@@ -6155,9 +6134,9 @@ namespace boost { namespace parser {
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success) const -> attribute_t<decltype(*first)>
|
||||
bool & success) const -> attribute_type<decltype(*first)>
|
||||
{
|
||||
attribute_t<decltype(*first)> retval;
|
||||
attribute_type<decltype(*first)> retval;
|
||||
call(use_cbs, first, last, context, skip, flags, success, retval);
|
||||
return retval;
|
||||
}
|
||||
@@ -6186,7 +6165,7 @@ namespace boost { namespace parser {
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
attribute_t<decltype(*first)> const x = *first;
|
||||
attribute_type<decltype(*first)> const x = *first;
|
||||
char32_t const x_cmp = x;
|
||||
if (x_cmp < U'\x0100' && (x_cmp < U'0' || U'9' < x_cmp)) {
|
||||
success = false;
|
||||
@@ -6292,7 +6271,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using attribute_t = std::decay_t<T>;
|
||||
using attribute_type = std::decay_t<T>;
|
||||
|
||||
template<
|
||||
bool UseCallbacks,
|
||||
@@ -6307,9 +6286,9 @@ namespace boost { namespace parser {
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success) const -> attribute_t<decltype(*first)>
|
||||
bool & success) const -> attribute_type<decltype(*first)>
|
||||
{
|
||||
attribute_t<decltype(*first)> retval;
|
||||
attribute_type<decltype(*first)> retval;
|
||||
call(use_cbs, first, last, context, skip, flags, success, retval);
|
||||
return retval;
|
||||
}
|
||||
@@ -6340,7 +6319,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
auto const & chars = detail::char_set<Tag>::chars;
|
||||
attribute_t<decltype(*first)> const x = *first;
|
||||
attribute_type<decltype(*first)> const x = *first;
|
||||
uint32_t const x_cmp = x;
|
||||
if (x_cmp < U'\x0100') {
|
||||
uint32_t const * it = std::lower_bound(
|
||||
@@ -6376,7 +6355,7 @@ namespace boost { namespace parser {
|
||||
constexpr char_subrange_parser() {}
|
||||
|
||||
template<typename T>
|
||||
using attribute_t = std::decay_t<T>;
|
||||
using attribute_type = std::decay_t<T>;
|
||||
|
||||
template<
|
||||
bool UseCallbacks,
|
||||
@@ -6391,9 +6370,9 @@ namespace boost { namespace parser {
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success) const -> attribute_t<decltype(*first)>
|
||||
bool & success) const -> attribute_type<decltype(*first)>
|
||||
{
|
||||
attribute_t<decltype(*first)> retval;
|
||||
attribute_type<decltype(*first)> retval;
|
||||
call(use_cbs, first, last, context, skip, flags, success, retval);
|
||||
return retval;
|
||||
}
|
||||
@@ -6422,7 +6401,7 @@ namespace boost { namespace parser {
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
attribute_t<decltype(*first)> const x = *first;
|
||||
attribute_type<decltype(*first)> const x = *first;
|
||||
char32_t const x_cmp = x;
|
||||
success = false;
|
||||
for (auto subrange : detail::char_subranges<Tag>::ranges) {
|
||||
@@ -8386,6 +8365,61 @@ namespace boost { namespace parser {
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename R, typename Parser>
|
||||
struct attribute_impl
|
||||
{
|
||||
using parser_type = typename Parser::parser_type;
|
||||
using global_state_type = typename Parser::global_state_type;
|
||||
using error_handler_type = typename Parser::error_handler_type;
|
||||
|
||||
using iterator = detail::iterator_t<R>;
|
||||
using sentinel = detail::sentinel_t<R>;
|
||||
|
||||
using context = decltype(detail::make_context(
|
||||
std::declval<iterator>(),
|
||||
std::declval<sentinel>(),
|
||||
std::declval<bool &>(),
|
||||
std::declval<int &>(),
|
||||
std::declval<error_handler_type>(),
|
||||
std::declval<global_state_type &>(),
|
||||
std::declval<detail::symbol_table_tries_t &>()));
|
||||
using type = decltype(std::declval<Parser>()(
|
||||
std::false_type{},
|
||||
std::declval<iterator &>(),
|
||||
std::declval<sentinel>(),
|
||||
std::declval<context>(),
|
||||
detail::null_parser{},
|
||||
detail::flags::gen_attrs,
|
||||
std::declval<bool &>()));
|
||||
};
|
||||
|
||||
template<typename Iter, typename Sentinel, typename Parser>
|
||||
auto has_attribute(Iter first, Sentinel last, Parser parser)
|
||||
{
|
||||
using attr_t = typename attribute_impl<
|
||||
BOOST_PARSER_SUBRANGE<Iter, Sentinel>,
|
||||
Parser>::type;
|
||||
return std::integral_constant<bool, !is_nope_v<attr_t>>{};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr wrapper<T> attr_wrapped_final;
|
||||
template<>
|
||||
inline constexpr wrapper<none> attr_wrapped_final<nope>;
|
||||
}
|
||||
|
||||
template<typename R, typename Parser>
|
||||
struct attribute
|
||||
{
|
||||
using initial_type = typename detail::attribute_impl<
|
||||
decltype(detail::make_input_subrange(std::declval<R>())),
|
||||
Parser>::type;
|
||||
using type =
|
||||
typename decltype(detail::attr_wrapped_final<initial_type>)::type;
|
||||
};
|
||||
|
||||
|
||||
namespace detail {
|
||||
template<typename... Args>
|
||||
constexpr void static_assert_merge_attributes(tuple<Args...> parsers)
|
||||
|
||||
@@ -43,6 +43,20 @@ namespace boost { namespace parser {
|
||||
constexpr bool enable_variant<std::variant<Ts...>> = true;
|
||||
#endif
|
||||
|
||||
/** A type trait that evaluates to the attribute type for parser `Parser`
|
||||
used to parse range `R`, as if by calling `parse(r, parser)`, using
|
||||
some `R r` and `Parser parser`. Note that this implies that pointers
|
||||
to null-terminated strings are supported types for `R`. The result is
|
||||
not wrapped in a `std::optional` like the result of a call to
|
||||
`parse()` would be. If `Parser` produces no attribute, the result is
|
||||
the no-attribute sentinel type `none`. */
|
||||
template<typename R, typename Parser>
|
||||
struct attribute;
|
||||
|
||||
/** An alias for `typename attribute<R, Parser>::type`. */
|
||||
template<typename R, typename Parser>
|
||||
using attribute_t = typename attribute<R, Parser>::type;
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
constexpr bool is_optional_v = enable_optional<T>;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,11 @@ void compile_or_attribute()
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<int>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<int>>);
|
||||
}
|
||||
|
||||
// scalar | scalar
|
||||
@@ -27,18 +32,33 @@ void compile_or_attribute()
|
||||
constexpr auto parser = char_ | char_;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(std::is_same_v<attr_t, std::optional<char>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
char>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = char_ | char_ | eps;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = int_ | char_;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::variant<int, char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::variant<int, char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = int_ | char_ | eps;
|
||||
@@ -46,6 +66,11 @@ void compile_or_attribute()
|
||||
static_assert(std::is_same_v<
|
||||
attr_t,
|
||||
std::optional<std::optional<std::variant<int, char>>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<std::variant<int, char>>>);
|
||||
}
|
||||
|
||||
// -scalar | -scalar
|
||||
@@ -54,12 +79,22 @@ void compile_or_attribute()
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = -char_ | -char_ | eps;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = -int_ | -char_;
|
||||
@@ -69,6 +104,11 @@ void compile_or_attribute()
|
||||
attr_t,
|
||||
std::optional<
|
||||
std::variant<std::optional<int>, std::optional<char>>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::variant<std::optional<int>, std::optional<char>>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = -int_ | -char_ | eps;
|
||||
@@ -78,6 +118,13 @@ void compile_or_attribute()
|
||||
attr_t,
|
||||
std::optional<std::optional<
|
||||
std::variant<std::optional<int>, std::optional<char>>>>>);
|
||||
static_assert(
|
||||
std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<
|
||||
std::variant<std::optional<int>, std::optional<char>>>>);
|
||||
}
|
||||
|
||||
// seq<T> | seq<T>
|
||||
@@ -85,18 +132,33 @@ void compile_or_attribute()
|
||||
constexpr auto parser = *char_ | *char_;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(std::is_same_v<attr_t, std::optional<std::string>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::string>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = *char_ | *char_ | eps;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<std::string>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<std::string>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = *string("str") | *string("str");
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::vector<std::string>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::vector<std::string>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = *string("str") | *string("str") | eps;
|
||||
@@ -104,6 +166,11 @@ void compile_or_attribute()
|
||||
static_assert(std::is_same_v<
|
||||
attr_t,
|
||||
std::optional<std::optional<std::vector<std::string>>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<std::vector<std::string>>>);
|
||||
}
|
||||
|
||||
// seq<T> | seq<U>
|
||||
|
||||
Reference in New Issue
Block a user