2
0
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:
Zach Laine
2024-02-25 15:31:18 -06:00
parent ed6e1b4a2c
commit 9ebc984ff8
6 changed files with 856 additions and 51 deletions

View File

@@ -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()`]]

View File

@@ -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]

View File

@@ -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)

View File

@@ -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

View File

@@ -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>